使用GC.AddMemoryPressure()防止OutOfMemoryException?

时间:2010-11-11 13:03:06

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

我正在调试一种方法,我们用它来标记带有特定文字的图像,然后再将它们显示在我们的系统中。

标签方法目前看起来像这样:

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;
}

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

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

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

首先我尝试在调用GC.Collect();之前做了一个通用的Graphics.FromImage(image),但是我不喜欢强制收集,因为它会对性能产生重大影响。

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

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

自从从服务中获取图像的整个操作后,我可能会使用GC.Collect() - >保存 - >标记是一个非常大的标记,所以收集的性能很小,但我真的想要一个更好的解决方案。

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

2 个答案:

答案 0 :(得分:17)

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

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

有关详细信息,请参阅http://msdn.microsoft.com/en-us/library/system.runtime.memoryfailpoint.aspx

答案 1 :(得分:10)

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

如果我的怀疑是正确的,那么当代码运行时,您将看到GDI Objects计数器让您的进程不断攀升。当它达到10,000时,Windows决定代码存在根本性的错误,并拒绝再创建句柄。 GDI +然后对它有点g and并报告内存不足错误。错了,应该是'无法创建句柄'错误。它没有的错误代码。 .NET无法改善异常。

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