使用GC.Collect()防止OutOfMemoryException

时间:2014-09-03 12:07:09

标签: c# garbage-collection out-of-memory task-parallel-library

我制作了一个处理图像的软件。它试图在图像中找到模式,如果它找到了它所寻找的模式,它会将图像名称,模式类型和坐标写入文本文件中。

您可能知道,图像处理需要很长时间。所以我决定使用多线程来提高性能。

bool Multithread = CheckMultithread();
UpdateParameters();

if (Multithread)
{
    Parallel.For(0, FileNames.Length ,i => Solve(FileNames[i]));
}
else
{
     foreach (string s in FileNames)
     {
         Solve(s);
     }
}

这是我第一次尝试在C#中使用多线程。但我这里没有多线程相关的错误,因为处理不会干扰另一个图像的处理。

问题是:如果多线程处于ON状态,当我到达第200个左右的图像时,我得到一个OutOfMemoryException ...显然,这种并行性(实际上整个代码在不同的线程中运行)消耗N倍的内存( N ==正在运行的线程数)...

我修改了我创建的每个类,只有一个使用非托管代码(QuickBitmap.cs,Bitmap类的包装器,使用lockbits和unlockbits来提高性能)。 但是我在那个类中实现了IDisposable接口,所以......我不知道为什么会这样。

每个线程需要+/- 400 mb的ram才能运行。 当抛出OutOfMemoryException时,程序使用大约1300 mb的ram。即使我有超过9 GB的可用内存

为了处理这个异常,我在Solve()

的求助中添加了以下代码
        if (GC.GetTotalMemory(false) > 1000*1000*1000)
        {
            lock (Manager.dasLock)
            {
                Manager.sw.Start();
                GC.Collect();
                Manager.sw.Stop();
            }
        }

ProcessedCount是一个 static int 在lock()语句中递增。

添加该段代码后,程序可以处理所有图像(2000+)而不会抛出任何异常。我也从不使用超过1.5 GB的ram。

但是因为每个人都因为DARING而打电话给全能的GC ...... 我能做些什么来防止这种情况?

如果你们想要,我可以发布更详细的代码。

Ps:在Solve()的最后几行中,我使用非托管资源调用每个对象的Dispose,并将所有变量设置为null。

Pps:是的,这是一个32位软件,但限制应该是4 GB而不是1.5,对吗?

Edit3:更改GC.Collect调用代码。这样我就可以知道我花了多少时间收集。对于每一分钟的处理,我都会花0.4秒收集。

2 个答案:

答案 0 :(得分:1)

限制线程数解决了问题:

        if (Multithread)
        {
            ParallelOptions pOptions = new ParallelOptions();
            pOptions.MaxDegreeOfParallelism = Environment.ProcessorCount;
            Parallel.For(0, FileNames.Length, pOptions, i => Solve(FileNames[i]));
        }
        else
        {
            foreach (string s in FileNames)
            {
                Solve(s);
            }
        }

答案 1 :(得分:0)

虽然没有说明,但如果您正在进行图像处理,那么您正在处理的某些对象很可能会实现IDisposable接口 - 例如File或Image对象。

对于所有这些对象,您应该尝试使用"使用"块(See here)。

更有可能的是,当你调用GC.Collect它正在处理这些对象,这就是我在你调用GC时所理解的原因。收集你没有得到例外