为什么itext7 for .NET比itextsharp5使用(更多)内存?

时间:2018-04-18 19:46:17

标签: c# .net itext pdf-generation

我正在使用itext在.NET环境中生成PDF。试图优化执行时间我正在从itextsharp 5.5.13迁移到.NET的itext 7.1.1。

生成的PDF主要包含图像。我使用多线程并行生成文档。

itext7似乎更快但内存使用率更高。由于我在同时生成多个文档,因此内存不足。

我使用相同的输入数据运行了一个简单的测试,输出文件是5 MB。下面是我的两个版本的库的代码。我的代码有问题吗?

itextsharp 5

时间:1:18,RAM:峰值173MB,然后稳定在65MB左右

public string GenerateImagesReport(IEnumerable<IChartData> data, string basename)
    {
        var doc = PdfUtility.CreateDoc();

        string path = Shared.BuildPdfPath(basename);
        using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write))
        {
            PdfWriter writer = PdfWriter.GetInstance(doc, fs);

            float left = 30f;
            float bottom = PdfUtility.GetYPosition(ReportElem.Chart2);
            float width = PdfUtility.CHART_WIDTH;
            float heigth = PdfUtility.CHART_hEIGTH * 2 + PdfUtility.V_SPACE1 + PdfUtility.V_SPACE2 + PdfUtility.GROUP_BY;

            doc.NewPage();
            doc.Open();
            PdfTemplate ImageTemplate;
            PdfContentByte cb = writer.DirectContent;
            Image img;
            foreach (var chart in data)
            {
                // chart image
                ImageTemplate = cb.CreateTemplate(width, heigth);
                img = Image.GetInstance(chart.ImageBytes, true);
                img.ScaleAbsolute(width, heigth);
                img.SetAbsolutePosition(0, 0);
                ImageTemplate.AddImage(img);

                cb.AddTemplate(ImageTemplate, left, bottom);

                chart.DestroyImage();
                doc.NewPage();
            }
            doc.Close();
        }

        return path;
    }

itext 7

时间:1:09,RAM:峰值753MB稳定到最后

public string GenerateImagesReport(IEnumerable<IChartData> data, string basename)
    {
        string path = Shared.BuildPdfPath(basename);
        using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write))
        {
            PdfWriter writer = new PdfWriter(fs);
            var pdf = new PdfDocument(writer);
            var pageSize = PageSize.LETTER;
            var doc = new Document(pdf, pageSize);

            float left = 30f;
            float bottom = PdfUtility.GetYPosition(ReportElem.Chart2);
            float width = PdfUtility.CHART_WIDTH;
            float heigth = PdfUtility.CHART_hEIGTH * 2 + PdfUtility.V_SPACE1 + PdfUtility.V_SPACE2 + PdfUtility.GROUP_BY;

            PdfPage page;
            PdfCanvas canvas;
            ImageData imgd;
            Image img;
            page = pdf.AddNewPage();
            foreach (var chart in data)
            {
                canvas = new PdfCanvas(page, true);

                imgd = ImageDataFactory.Create(chart.ImageBytes);
                img = new Image(imgd, left, bottom);
                img.ScaleAbsolute(width, heigth);

                new Canvas(canvas, pdf, pageSize)
                    .Add(img);

                chart.DestroyImage();
                page = pdf.AddNewPage();
            }

            doc.Close();
        }

        return path;
    }

更新

我正在使用Visual Studio Profiler来监控内存使用情况。在Yaroslav Veremenko的意见之后,我看到内存使用情况有所改善。当生成pdf的实际过程开始时,不同的图表已经标记出来。

with itextsharp 0:43 itextsharp memory usage

使用itext7 0:26 itext7 memory usage

使用itext7

- page.Flush()0:42 itext7 memory usage 2

2 个答案:

答案 0 :(得分:3)

我对这个库并不熟悉,但可能是PdfCanvasCanvas个对象在被使用后没有被销毁,并且在文档被销毁之前一直存在于内存中。 根据文档,您必须在绘制图表后释放内存。

  

确保在写完后调用PdfCanvas.release()   画布。

来源:https://github.com/itext/itext7-dotnet/blob/dd5c209cff35c137ed451fef6e11a96889a52fe9/itext/itext.kernel/itext/kernel/pdf/canvas/PdfCanvas.cs#L69

<强> UPD

我刚刚在本地运行它。在我的例子中,我有500MB的高峰。我添加后:

page.Flush(true);

它下降到250MB。

参考:http://itextsupport.com/apidocs/itext7/7.0.2/com/itextpdf/kernel/pdf/PdfPage.html#flush-boolean-

<强> UPD2

使用和不使用page.Flush(true)

的内存使用情况
    foreach (var chart in Enumerable.Range(0, 10))
    {
        canvas = new PdfCanvas(page, true);
        imgd = ImageDataFactory.Create((byte[])converter.ConvertTo(data, typeof(byte[])));
        img = new iText.Layout.Element.Image(imgd, left, bottom);
        img.ScaleAbsolute(width, heigth);
        new Canvas(canvas, pdf, pageSize)
            .Add(img);
        // this line has been added
        page.Flush(true);
        page = pdf.AddNewPage();
    }

graph

答案 1 :(得分:1)

最重要的是将PdfWriter包裹在using中,因为这会因为某个原因而实现IDisposable,并且在并行使用时应该会有很大的帮助。我还删除了Canvas的实例化,因为当您现有的PdfCanvas对象可以做同样的事情时,它似乎是不必要的和浪费的。此外,我已将您的字段移至Foreach范围,以便GC更有可能收集这些字段。

using (var fs = new FileStream(path, FileMode.Create, FileAccess.Write))
{
    using (PdfWriter writer = new PdfWriter(fs)) //**Implements IDisposable - This should help hugely when used in Parallel**
    {
         var pdf = new PdfDocument(writer);
         var pageSize = PageSize.LETTER;
         var document = new Document(pdf);

          foreach (var chart in data)
          {
             var page = pdf.AddNewPage(); //When it get's reassigned on the next iteration, Garbage collection will take over
             PdfCanvas canvas = new PdfCanvas(page, true); //When it get's reassigned on the next iteration, Garbage collection will take over
             canvas.AddImage(ImageDataFactory.Create(chart.ImageBytes), pageSize, false); //1x Less Object in Memory but you will need to play around with params for precision.
             chart.DestroyImage();
          }

          document.Close();
    }
}