【问题标题】:Preventing OutOfMemoryException with GC.AddMemoryPressure()?使用 GC.AddMemoryPressure() 防止 OutOfMemoryException?
【发布时间】:2010-11-11 13:03:05
【问题描述】:

我目前正在调试一种方法,用于在将图像显示在系统中之前用特定文本标记图像。

标签方法现在是这样的:

private static Image TagAsProductImage(Image image)
{
    try
    {
        // Prepares the garbage collector for added memory pressure (500000 bytes is roughly 485 kilobytes).
        // Should solve some OutOfMemoryExceptions.
        GC.AddMemoryPressure(500000);

        using (Graphics graphics = Graphics.FromImage(image))
        {
            // Create font.
            Font drawFont = new Font("Tahoma", image.Width*IMAGE_TAG_SIZE_FACTOR);

            // Create brush.
            SolidBrush drawBrush = new SolidBrush(Color.Black);

            // Create rectangle for drawing.
            RectangleF drawRect = new RectangleF(0, image.Height - drawFont.GetHeight(), image.Width,
                                                    drawFont.GetHeight());

            // Set format of string to be right-aligned.
            StringFormat drawFormat = new StringFormat();
            drawFormat.Alignment = StringAlignment.Far;

            // Draw string to screen.
            graphics.DrawString(TAG_TEXT, drawFont, drawBrush, drawRect, drawFormat);
        }
    }
    // If an out of memory exception is thrown, return the unaltered image.
    catch(OutOfMemoryException)
    {
        GC.RemoveMemoryPressure(500000);
        return image;
    }

    GC.RemoveMemoryPressure(500000);
    return image;
}

在上下文中:在从我们的图像服务器检索图像并保存到本地缓存(我们的系统与需要相同图片的其他系统共享)之后调用此方法。

我们在到达using (Graphics... 时遇到了OutOfMemoryExceptions 的问题(当需要在标记之前从服务器检索图像时,如果图像存在于缓存中,则标记没有问题)。

为了防止/规避 OutOfMemoryException,我尝试了三种不同的方法,虽然它们有效,但我并不喜欢其中任何一种。

首先,我尝试在调用 Graphics.FromImage(image) 之前执行通用的 GC.Collect(); (当然),但我不喜欢强制收集,因为它会对性能造成很大影响。

我的第二种方法是在 catch 语句中调用 GC.Collect(),然后递归调用 TagAsProductImage(image),但如果 GC 无法释放足够的内存,这可能会导致无限循环。

最后我得到了上面的代码,我不能说我很喜欢。

我可能可以不使用GC.Collect(),因为从服务获取图像 -> 保存 -> 标记的整个操作非常大,因此收集的性能影响很小,但我真的喜欢更好的解决方案。

如果有人对此有聪明的解决方案,请分享。

【问题讨论】:

    标签: c# garbage-collection out-of-memory


    【解决方案1】:

    如果您正在寻找一种方法来确保有足够的内存用于操作,请使用MemoryFailPoint

    通过using,您可以定义一个需要一定内存量的区域。如果这不可用,它将抛出一个可恢复的InsufficientMemoryException

    更多信息请参见http://msdn.microsoft.com/en-us/library/system.runtime.memoryfailpoint.aspx

    【讨论】:

    • 我不知道 MemoryFailPoint,所以感谢您的提醒,它直接进入我的“有用的代码记住”角落。不幸的是,它没有帮助,我尝试分配从 1 到 200 MB 的所有内容,它在所有情况下都成功,但在尝试创建 Graphics 对象时仍然抛出 OutOfMemoryException。将不得不继续寻找(GDI+ 和内存处理有时会很痛苦)。
    • 我认为它失败了它并不总是失败???你应该做的是在使用之前,做一个GC.GetTotalMemory(false),然后在使用块结束之前,另一个。差异应该是用于该进程的实际内存量。如果提供 MemoryFailPoint 的金额不起作用,那么,我没有想法了:P。
    • 你是绝对正确的,只有当图像以前不在缓存中但在调用此方法之前从我们的图像服务器检索时才会失败。奇怪的是MemoryFailPoint 设法分配内存(每次!)但如果图像以前不在缓存文件夹中,using (Graphics... 无论如何都会抛出 OutOfMemoryException。
    • 图片有多大? bpp和尺寸?而且我无法真正放置来自缓存的位置。如果Image 不是来自缓存(失败的情况),它是如何创建的?
    • 图像通常小于 200 kB。如果它们没有存储在本地(即在应用程序的 cahce 文件夹中),它们会从服务中检索,然后另存为 PNG,但它们可能会以 jpg 或 png 的形式到达标记方法(如果它们以 jpg 格式下载,它们不存储,然后从本地文件夹中检索)。即使这个特定的答案没有解决我的问题,我也会将此标记为答案,因为我真的在寻找我现在正在做的事情的替代方案,而您的解决方案似乎是一个不错的选择。
    【解决方案2】:

    您在这里遇到了不同的问题,此代码使用非常很少的内存。可悲的是,GDI+ 异常非常糟糕。使用 TaskMgr.exe 的“进程”选项卡进行诊断。查看 + 选择列并勾选 GDI 对象、句柄和用户对象。

    如果我的怀疑是正确的,您将看到 GDI 对象计数器随着此代码的运行而不断攀升。当它达到 10,000 个时,Windows 确定代码存在根本性错误,并拒绝创建更多句柄。然后 GDI+ 对此感到有些不安,并报告了内存不足的错误。错了,应该是“无法创建句柄”错误。它没有的错误代码。 .NET 无能为力改善异常。

    Anyhoo,原因是您没有在字体和画笔上调用 Dispose()。用 using 语句包装它们。这通常不会造成问题,但您的程序显然使用的垃圾回收内存太少,无法启动终结器线程。

    【讨论】:

    • 我包含的代码只是一个非常大的类中的一个小方法(请参阅在哪个上下文中调用该方法的描述),而该类又是一个非常大的类的一部分具有许多用户的数据繁重的系统。我已经设法在这个特定的方法(使用 perfmon、taskmanager 等)中将此特定错误跟踪到 OutOfMemoryException,除此之外,在使用画笔或字体之前抛出异常,并且因为我将它们放在 using 中Graphics 对象时它们应该被释放的图形。他们不应该吗?但我确实同意 GDI+ 例外很糟糕。
    • 不,Graphics 不会自动处理任何内容。
    • 即使放入using 语句也不行?我一直认为在 using 语句中放入的任何内容在退出 using 范围时都会被释放。
    • 这只会处理 Graphics 对象,而不是用于实际绘制的任何绘图对象。 Graphics 对象无法跟踪您创建的内容,请注意您在创建 Font 时没有传递对它的引用。
    • 感谢您澄清这一点,我现在在课堂上的任何地方都可以添加.Dispose()。这可能会为这个方法腾出足够的空间。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-27
    • 1970-01-01
    • 1970-01-01
    • 2011-08-06
    • 2011-10-06
    • 1970-01-01
    相关资源
    最近更新 更多