【问题标题】:VB.NET printing images using a lot of memoryVB.NET 打印图像使用大量内存
【发布时间】:2012-06-20 17:26:38
【问题描述】:

我正在使用在代码项目中找到的 DLL 在 VB.NET 中处理 PDF:

http://www.codeproject.com/Articles/37458/PDF-Viewer-Control-Without-Acrobat-Reader-Installe

我的应用程序允许您在网格中选择多个文件并打印它们。这些文件存储在受密码保护的 zip 文件中,因此我要做的第一步是将每个选定的文件提取到我传递给新 PDF 包装器对象的内存流中。每个对象都被添加到队列中。然后,将队列中的每个对象逐页打印为 system.drawing.image。整个事情在后台工作人员上运行。

现在,将 PDF 提取到队列中几乎不会占用任何内存。但是在 PrintPage 事件处理程序中,当我提取图像并将它们发送到打印机时,一定有问题。我的内存使用量激增。当然,每个图像都很大,因为它以 300 dpi 渲染,但每个页面使用的内存没有返回给操作系统,也没有被垃圾收集。

最后,如果我选择了足够多的文件,我的内存就会用完。为什么?

【问题讨论】:

  • 在问题中包含代码的一些相关部分可能会帮助您获得问题的答案。
  • 忘记调用 Image.Dispose() 方法是一个标准错误。
  • 当您处理图形或位图时,最好在每次通过后处理实例,这样它就不会积累无用的内存。只需使用 Dispose 方法
  • 我正在调用 dispose,但内存从未释放。

标签: vb.net pdf printing


【解决方案1】:

好吧,我终于想通了。

首先,就图像而言,CLR 显然不知道为 Drawing.Image 分配了多少内存,所以当你处理它时,你必须告诉它:

'It's 4 bytes per pixel with RGBA
'Use Drawing.Image.PixelFormat to get
'the number of bytes if you don't know
Dim countBytes as long = 4 * img.Width * img.Height

'Let the CLR know of the memory we want to free
if countBytes > 0 then GC.AddMemoryPressure(countBytes)

'Get rid of the image
img.Dispose()
img = Nothing

'Free up the unused memory
GC.Collect()

'Tell the CLR we took care of it
GC.RemoveMemoryPressure(countBytes)

现在,CodeProject 示例中的 PDF 库要困难得多。

首先,确保在包含包装器的表单的FormClosed 事件或包含它的类的Finalize 方法中调用PDFWrapper 对象上的Dispose 方法.

但是,PDFWrapper 实际上似乎缓存了您从中检索到的图像。因此,当您翻阅 PDF 时,内存使用量会增加,直到整个 PDF 的图像都被缓存。如果您使用这些图像以 300DPI 打印 PDF,这将是一个更大的问题(在使用 1.5GB 内存的情况下,我在 60 多页 PDF 的末尾出现内存不足错误)。

据我所知,该对象没有“清除缓存”方法。但是我用来让它工作的技巧是在我得到我需要的图像后以 1DPI 抓取图像,然后像上面那样执行垃圾收集。这间接释放了缓存的内存。但是,像以前一样,我们必须告诉 CLR 我们使用了多少字节。和上面的计算是一样的。

但是还有一个问题。 PDFWrapper 对象实际上是在另一个线程上抓取图像,看起来。因此,通过在我们请求 300DPI 图像之后请求另一个 1DPI 图像,它会感到困惑并随机吐出 1DPI 图像,而它应该给我们 300DPI 图像来打印。因此,解决方法:

Dim img As System.Drawing.Image
img = AFPDFLibUtil.GetImageFromPDF(pdfWrapper, currentPage, DPI)

'Wait for PDFWrapper to finish rendering
Dim sw As New Stopwatch()
sw.Start()
While _pdfWrapper.IsBusy
    If sw.ElapsedMilliseconds < TimeoutMS Then
        System.Threading.Thread.Sleep(10)
    Else
        Throw New Exception("This page took too long to render.")
    End If
End While
sw.Stop()
sw.Reset()

然后就可以了。也许这就是为什么在 CodeProject 示例中,他使用不同的 DLL 进行打印。但是,PDFWrapper 对象支持从 IO.MemoryStream 读取,我认为该项目中的任何其他包含都不支持。

祝所有阅读本文的人编码愉快!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-06
    • 2021-06-26
    • 2011-09-28
    • 1970-01-01
    相关资源
    最近更新 更多