我知道有很多关于这方面的话题,这通常是一个坏主意,但这是一个相对具体的案例。除了GC.Collect之外,我唯一能够解决问题的方法是“减慢”应用程序,或者使用更少的线程。我已经复制了我的应用程序实际执行的一小部分内容,因此我可以在本地重新创建该问题。以下代码非常快速地获得$ OutOfMemoryException。 *注意我没有包含它获取byte [] i1的部分,但它非常通用。
private static byte[] i1;
private static IList<Guid> jobs;
static void Main(string[] args)
{
jobs = new List<Guid>();
StartThreads(5);
Console.ReadLine();
}
public static void StartThreads(int maxThreads)
{
while (true)
{
tcount ++;
if (jobs.Count >= maxThreads) continue;
ThreadStart ts = (ProcessImage);
var workerThread = new Thread(ts) { IsBackground = true };
workerThread.Start();
}
}
public static void ProcessImage()
{
var guid = Guid.NewGuid();
try
{
lock (jobs)
{
jobs.Add(guid);
}
var x = ByteArrayToImage(i1);
var y = ByteArrayToImage(i1);
OverlayImages(x, y);
}
catch (Exception e)
{
Console.WriteLine(e.StackTrace);
}
finally
{
lock (jobs)
{
jobs.Remove(guid);
}
// GC.Collect();
}
}
private static void OverlayImages(Image ymcImage, Image kImage)
{
var attr = new ImageAttributes();
attr.SetColorKey(Color.White, Color.White);
Graphics g = Graphics.FromImage(ymcImage);
var destRect = new Rectangle(0, 0, ymcImage.Width, ymcImage.Height);
var templateBmp = new Bitmap(kImage);
g.DrawImage(templateBmp, destRect, 0, 0, kImage.Width, kImage.Height, GraphicsUnit.Pixel, attr);
g.Dispose();
}
public static Image ByteArrayToImage(byte[] image)
{
using (MemoryStream ms = new MemoryStream(image))
{
return Image.FromStream(ms);
}
}
如果我将最大线程数减少到5,我可以看到(通过观察任务管理器中的进程)内存增加,大约每9个“滴答”(当任务管理器更新时,每个大约1秒)或者它)它回收了大部分。在5或更高的情况下,内存似乎在前9个“滴答”之前达到致命级别(大约1.6G)并且崩溃。在4个线程中,我可以看到内存增加到1G以上,然后回到大约70M,并重复。线程越少,收集前的时间就越少。如果我取消注释GC.Collect,我可以运行20个线程,内存保持在100到200之间。
我也一直在玩ByteArrayToImage中的各种事情来尝试不使用这么多内存,但我已经在这方面取得了真正的成功。这包括克隆图像并将其返回到使用之外并删除using语句
此外,我的应用程序无法快速完成,并且很难确切地说出发生了什么。我无法在测试环境中重现完整应用程序中的重负载。但它每天都会得到一次OutOfMemoryException。它将连续几次并自行恢复,但在此过程中会丢失一些工作。我不确定这是否能解决我的问题,但这是我在测试中遇到的问题。我想我只是想知道其他有更多专业知识的人会想到这个,如果有更好的选择,而不是自己调用GC。