使用GC.AddMemoryPressure()防止OutOfMemoryException?

Man*_*sen 16 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;
}
Run Code Online (Sandbox Code Playgroud)

为了把事情背景:之后的图像已经从我们的图像服务器中检索并保存到本地缓存被调用此方法(即我们的系统股,需要相同的图片等系统).

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

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

首先,我尝试GC.Collect();在调用之前做一个通用Graphics.FromImage(image)工作(当然),但我不喜欢强制收集,因为它在性能上留下了很大的打击.

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

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

我可以放弃使用,GC.Collect()因为从服务中获取图像的整个操作 - >保存 - >标记是相当大的,因此来自收集的性能损失将是最小的但我真的想要一个更好的解决方案.

如果有人有这方面的智能解决方案,请分享.

Pie*_*kel 17

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

有了这个,通过a using,您可以定义一个需要一定内存量的区域.如果没有,它将是可恢复的InsufficientMemoryException.

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


Han*_*ant 10

你在这里有一个不同的问题,这段代码使用的内存非常少.可悲的是,GDI +异常非常糟糕.使用TaskMgr.exe,进程选项卡进行诊断.查看+选择列并勾选GDI对象,句柄和用户对象.

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

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

  • 不,Graphics不会自动处理任何东西. (2认同)