【问题标题】:Out Of Memory exception on System.Drawing in a Windows ServiceWindows 服务中 System.Drawing 的内存不足异常
【发布时间】:2011-01-09 12:34:50
【问题描述】:

在我的 Windows 服务中密集使用 system.drawing 时出现内存不足异常。

这是我的代码的一部分:

FileStream fs = new FileStream(ImagePath, FileMode.Open, FileAccess.Read);
img = (Image)Image.FromStream(fs).Clone();

该异常不仅在这一点上提出,有时在涉及绘图、裁剪、调整大小、图像复制的其他点上也提出

我总是处理所有使用过的对象。

我在system.drawing中看到了这篇关于“Out of memory exception”的文章 here

虽然它与我的情况非常相似,但不同的是,因为我的测试数据是 40 张图像的重复链,所以如果其中一张出现问题,它应该从第一个周期开始很快出现。

我看到here提到的限制

它说我们不应该在 Win/Web 服务上使用 system.drawing。

好的,我需要将它与 Windows 服务一起使用来处理大量图像,如果有的话,还有什么替代方案?

【问题讨论】:

  • 我编写了一个使用 System.Drawing 没有任何问题的 Windows 服务。建议禁止的原因是仅当您绘制到桌面时......换句话说,Windows 服务不应该尝试绘制到屏幕上,因为 Windows 服务不一定有登录用户,因此没有要绘制到的桌面。
  • 事实上,MSDN 中解释说您“可以”在 Windows 服务中使用 system.drawing,但它们不保证其行为。 forums.asp.net/t/1125885.aspx我的服务运行了几个小时没有问题,处理了几千张图像,但随后发生了这个异常。

标签: graphics memory-management windows-services drawing system.drawing


【解决方案1】:

这可能不相关,但是...

我在使用 C++ 中的位图时遇到问题,从较低级别的 Windows API 获得资源不足错误。这似乎是由于在使用 bitblts 等进行绘制时,Windows 内部资源堆无法处理/分配位图/位图部分。

不知道链条中的类似情况是否会影响 dotnet 库。 MSDN 上有一个安装设备驱动程序的工具,可用于检查各种桌面堆(不知道这可能与服务有什么关系。)我认为(我使用它已经有一段时间了)它显示了使用的百分比/available - 虽然这可能是一些指标,但我怀疑这些区域的碎片也会导致错误 - 不幸的是我无法完全消除我的问题,但该工具可能会帮助您诊断您的问题。 (看起来该工具可能被称为“dheapmon”。)

【讨论】:

    【解决方案2】:

    您调用CloneImage 对象没有被释放(并且您的代码中可能还有其他类似的错误)。此类对象消耗的内存仅在终结器运行后才会释放,这需要至少 2 个 GC 周期,并且OutOfMemoryException 很可能会在第一次 GC 时被抛出。
    正确处理它应该类似于:

    FileStream fs = new FileStream(ImagePath, FileMode.Open, FileAccess.Read);
    using(var tmp = Image.FromStream(fs))
      img = (Image)tmp.Clone();
    // dispose fs an img after use like usually
    

    或者,您可以将整个文件读入MemoryStream 并将其传递给Image.FromStream - 这意味着图像不会被解析两次,这应该会节省额外的资源。

    【讨论】:

    • 好点,我训练有素的头脑很难想象这样的事情可以完成。 :)
    • 你的意思是名为“img”的对象???我在完成处理后处理它。我也在这段代码之后处理流。如果我不调用 clone(),那么处理流将使图像为 Null。检查我之前发布的这个问题:stackoverflow.com/questions/1951813/…
    • 我的意思是 FromStream(fs) 调用创建的对象(您正在克隆的那个) - 释放流不会释放创建的 Image 对象。
    【解决方案3】:

    你使用太多内存太快了,可能没有尽快释放它。

    您需要重构您的代码以更加保守地使用您的资源,并确保您持有大型对象的时间不会超过所需时间,并且您要尽早处理您的 IDisposables。

    这个过程并不容易,也不能在这样的论坛上完全回答。

    【讨论】:

    • 我在性能计数器或任务管理器中看不到这么多内存使用情况,是的,我正在使用大量内存,但我认为它不足以导致此异常,除非 system.绘图类使用的内存超出预期
    • 您没有使用太多内存,但您遇到了 OOM 异常。这里有些不对劲。没有任何奇怪的事情发生;操作系统的这些部分(本机绘图代码)和 CLR/GC 没有问题。您需要放弃所有假设,从头开始并三重检查所有内容。哦,任务管理器很烂。至少从 Sysinternals 获取 Process Explorer,并使用 PERFORMANCE COUNTERS msdn.microsoft.com/en-us/magazine/cc163491.aspxmsdn.microsoft.com/en-us/magazine/cc163528.aspx
    • 啊,错过了。看到“任务管理器”,顿时怒火中烧。我的错。
    • @Will:这些组件的error->异常转换肯定是有问题的,它们会抛出OutOfMemoryException 用于各种非内存错误。
    【解决方案4】:

    为什么要克隆图像?在您的示例中,您正在从文件中读取数据,然后创建一个副本。在具有高对象吞吐量的服务中抛出内存不足异常并不需要太多。

    你可以这样做:

    var img = Image.FromFile(ImagePath);
    

    【讨论】:

    • 我在该代码之后立即处理流,如果我不克隆它,那么处理流也会使图像为 Null。我不能使用 Image.FromFile 因为它在整个处理过程中保存了文件,这意味着在处理过程中不能移动或删除它,这在我的情况下不被接受。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-09-13
    • 1970-01-01
    • 2015-02-16
    • 2014-05-22
    • 2010-10-05
    相关资源
    最近更新 更多