C# 使用GC.AddMemoryPressure()防止OutOfMemoryException?

C# 使用GC.AddMemoryPressure()防止OutOfMemoryException?,c#,garbage-collection,out-of-memory,C#,Garbage Collection,Out Of Memory,我目前正在调试一种方法,在系统中显示图像之前,我们使用该方法用特定文本标记图像 tag方法目前看起来如下所示: private static Image TagAsProductImage(Image image) { try { // Prepares the garbage collector for added memory pressure (500000 bytes is roughly 485 kilobytes). // Should

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

tag方法目前看起来如下所示:

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;
}
将事情放在上下文中:从图像服务器检索图像并保存到本地缓存(我们的系统与需要相同图片的其他系统共享)后,将调用此方法

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

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

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

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

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

我可能可以使用
GC.Collect()
,因为从服务->保存->标记中获取图像的整个操作都是一个相当大的操作,因此Collect对性能的影响将是最小的,但我真的希望有一个更好的解决方案


如果有人对此有智能解决方案,请与我们分享。

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

这样,通过使用
,您可以定义一个需要一定内存量的区域。如果该区域不可用,它将抛出可恢复的
内存不足异常


有关详细信息,请参阅。

您遇到了另一个问题,此代码使用的内存非常少。遗憾的是,GDI+异常非常糟糕。请使用TaskMgr.exe的“进程”选项卡诊断此问题。查看+选择列并勾选GDI对象、句柄和用户对象

如果我的怀疑是正确的,那么您将看到,当此代码运行时,进程的GDI对象计数器不断攀升。当它达到10000个时,Windows认为代码存在根本性错误,并拒绝创建任何更多句柄。GDI+然后会对此感到有点松懈,并报告内存不足错误。错误,应该是这样的出现了“无法创建句柄”错误。它没有错误代码。.NET无法改进此异常


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

我包含的代码只是一个相当大的类中的一个小方法(请参阅调用该方法的上下文的描述),而该类又是一个数据量非常大的系统的一部分,有许多用户。我已设法将此特定错误跟踪到该特定方法中的OutOfMemoryException(使用perfmon、taskmanager等)最重要的是,在使用画笔或字体之前会抛出异常,因为我将它们放在图形的using中,所以当图形对象是时,它们应该被处理。不是吗?但我同意GDI+异常是蹩脚的。不,图形不会自动处理任何东西。即使在放入
using
语句时也不会。我不同意I’我一直认为,在using语句中放入的任何内容都是在退出using作用域时处理的。这只处理图形对象,而不是实际绘制时使用的任何图形对象。图形对象无法跟踪您创建的内容,请注意,例如,您在创建字体时没有传递对它的引用。谢谢您的清除至此,我将在类中的所有位置添加
.Dispose()
。这可能会为该方法腾出足够的空间。我不知道MemoryFailPoint,因此感谢您的提醒。它将直接转到我的“要记住的有用代码”不幸的是,它没有帮助,我尝试分配从1到200 MB的所有内容,在所有情况下都成功了,但在尝试创建图形对象时仍然抛出OutOfMemoryException。将不得不继续查找(GDI+和内存处理有时会很痛苦)。我认为它失败了。它并不总是失败???你应该做的是在使用之前,做一个
GC.GetTotalMemory(false)
在using块结束之前,还有另一个。区别应该是用于该过程的实际内存量。如果将该量提供给
MemoryFailPoint
不起作用,那么,好吧,我没有主意了:P.你完全正确,只有当图像以前不在缓存中,但被重新存储时,它才会失败在调用此方法之前从我们的图像服务器上看到。奇怪的是
MemoryFailPoint
管理分配内存(每次!),但
使用(Graphics…
如果图像以前不在缓存文件夹中,则会抛出OutOfMemoryException。图像有多大?bpp和尺寸?我无法真正从缓存中放置图像。
图像如果不是从缓存中创建的(失败案例)?图像通常小于200 kB。如果图像不是本地存储的(即,在应用程序的cahce文件夹中),则从服务中检索图像,然后将其保存为png,但它们可能以jpg或png的形式到达标记方法(如果图像是作为jpg下载的,则不会存储,然后从本地文件夹中检索).即使这个特定的answ