我在c#windows窗体程序中经历了一个奇怪的内存堆栈,它一直出现在慢速PC上,当窗口失去焦点或者在更快的PC上中断时。
我编写的程序使用Aforge从我的网络摄像头获取图像,然后我将其显示在Windows窗体中的Aforge图片框控件(代码中的CurrImagePic)中。图像被切换到图片框中,然后以相机的原始帧速率处理,因此它显示为用户的视频,而不是静止图像。图片框为1080x1920,但表格中的空间较小,因此我允许用户滚动图片。
在较慢的PC上进行大约~30秒的内存稳定操作后,问题就开始了。在速度较快的PC上,只有在按住滚动条箭头或单击并拖动任一滚动条时,如果我锁定PC或调出Ctrl + Alt + Delete菜单,才会出现此问题。
问题本身是程序使用的内存在非常大的块中开始增加,导致内存不足崩溃。这在较慢的PC上是不可阻挡的,但是如果你停止滚动或从lock / Ctrl + alt + delete菜单返回,在较快的PC上,程序将稳定在较高的内存使用级别。滚动期间或锁定菜单中累积的内存永远不会被垃圾收集器收集。我甚至试图在按下时按下一个强制GC.collect()的按钮,它不会减少这种内存使用量。
我运行了perfmon并发现内存增加是在非托管堆上,但我不知道它是否来自未被处理的位图或它可能来自的位图。追踪是不可能的,因为除了上述条件之外它不会发生。我尝试了各种解决方案(比如将我的图像处理移出事件处理程序,甚至使用全局标志和“lock”语句来尝试确保只有一个线程或帧可以访问图像处理和显示方法但是我没有看到任何变化。事实上,我现在看到一些无法解释的内存使用小跳跃,在我放入锁定并将处理移出处理程序之前我没有看到。
有没有人遇到过这样的情况?我对我需要解决的问题感到茫然,而且我从Aforge论坛得到的帮助并不多。我认为这个问题是基于我的Aforge事件处理程序和图像处理方法,如果它完全在我的代码中 - 但我也怀疑这是Windows窗体代码中更深层次的东西,我要么误用,要么不能跟上我的代码的要求。代码如下:
//Applicable Globals to this code snippet
private bool ALLOWFRAME = true;
private Object FRAMEKEY = new Object();
private VideoCaptureDevice COMPVID;
private Bitmap TMPLTCAP;
private System.Drawing.Image OLDIMAGE;
private bool RCRDPIC = false;
private void COMPVID_NewFrame(object sender, NewFrameEventArgs eventArgs)
{
//Only process a frame when another is done processing
if (ALLOWFRAME == true)
{
ALLOWFRAME = false;
Bitmap PassFrame = AForge.Imaging.Image.Clone(eventArgs.Frame);
ProcessFrame(PassFrame);
PassFrame.Dispose();
}
}
private void ProcessFrame(Bitmap frameIn)
{
lock (FRAMEKEY)
{
if (OLDIMAGE != null) { OLDIMAGE.Dispose(); }
//Call comparison method if flag is set.
if (COMPON == true)
{
Difference TmpltFilter = new Difference(TMPLTCAP);
TmpltFilter.ApplyInPlace(frameIn);
OLDIMAGE = CurrImagePic.Image;
CurrImagePic.Image = AForge.Imaging.Image.Clone(frameIn);
OLDIMAGE.Dispose();
}
else
{
OLDIMAGE = CurrImagePic.Image;
CurrImagePic.Image = AForge.Imaging.Image.Clone(frameIn);
OLDIMAGE.Dispose();
//Toggle the flag back to false to show it's safe (i.e., comparisons have stopped)
//for the result-recording method to copy from the picture box if it is attempting to copy
if (RCRDPIC == true)
{
RCRDPIC = false;
}
}
ALLOWFRAME = true;
}
}
答案 0 :(得分:1)
经常提高性能的一种方法是将图像排队到内存中,并使用计时器控件在图片框中将它们出列/显示。通过这种方式,您可以控制正确的处理,并允许NewFrame
事件更快地返回,而不是在图像处理中被束缚。
此外,在Timer_Tick
事件中,请尝试执行以下操作:
this.Timer.Stop();
Bitmap image = null;
var temp = this.PictureBox.Image;
lock (FRAMEKEY)
{
if (this.ImageQueue.Any())
{
image = this.ImageQueue.Dequeue();
if (temp != null) { temp.Dispose(); }
}
}
this.PictureBox.Image = image;
if (temp != null) { temp.Dispose(); }
this.Timer.Start();