我正在制作一个小型应用程序,以窗体形式显示实时网络摄像头,并且还存储水印图像以指定间隔驱动(创建间隔拍摄视频是最终目标).
我正在使用AForge库进行图像和视频处理.
我有问题,似乎有内存泄漏,即使我试图确保在图像处理发生的每个位置使用“使用”语句.
下面是进行图像处理的代码(The NewFrame事件)
private void Video_NewFrame(object sender, NewFrameEventArgs eventArgs)
{
try
{
if (ImageProcessing) // If the previous frame is not done processing, let this one go
return;
else
ImageProcessing = true;
using (Bitmap frame = (Bitmap)eventArgs.Frame)
{
// Update the GUI picturebox to show live webcam feed
Invoke((Action)(() =>
{
webcam_PictureBox.Image = (Bitmap)frame.Clone();
}));
// During tests, store images to drive at a certain interval
if (ImageStoreTimer.Elapsed.TotalSeconds > ImageStoreTime)
{
DateTime dt = DateTime.Now;
using (Graphics graphics = Graphics.FromImage(frame))
{
PointF firstLocation = new PointF(frame.Width / 2, frame.Height / 72);
PointF secondLocation = new PointF(frame.Width / 2, frame.Height / 15);
StringFormat drawFormat = new StringFormat();
drawFormat.Alignment = StringAlignment.Center;
using (Font arialFont = new Font("Arial", 15))
{
graphics.DrawString(dt.ToString(), arialFont, Brushes.Red, firstLocation, drawFormat);
graphics.DrawString(Pressure.ToString("F0") + " mbar", arialFont, Brushes.Red, secondLocation, drawFormat);
}
}
// Place images in a folder with the same name as the test
string filePath = Application.StartupPath + "\\" + TestName + "\\";
// Name images by number 1....N
string fileName = (Directory.GetFiles(filePath).Length + 1).ToString() + ".jpeg";
frame.Save(filePath + fileName, ImageFormat.Jpeg);
ImageStoreTimer.Restart();
}
}
//GC.Collect(); <----- I dont want this
}
catch
{
if (ProgramClosing == true){}
// Empty catch for exceptions caused by the program being closed incorrectly
else
throw;
}
finally
{
ImageProcessing = false;
}
}
现在,在运行程序时,我看到内存使用量上下变化,通常在下降之前达到大约900MB.但偶尔它会升至2GB.偶尔,我甚至在这一行得到一个内存不足的例外:
Graphics graphics = Graphics.FromImage(frame)
因此,花了一个小时左右尝试重塑代码并寻找内存泄漏后,我终于尝试了代码中注释掉的GC.Collect行(Shame).之后,我的内存使用率保持不变,低于60MB.我可以毫不费力地运行该程序24小时.
所以我读了一下关于GC.Collect的内容,以及它有多糟糕,例如它可能需要很多处理能力才能在程序中经常使用它.但是,当我比较我的程序使用的CPU功率时,无论我是否评论该线路或离开它,它都不会真正改变.但是如果我在新的帧事件结束时收集内存问题就消失了.
我想找到一个不涉及GC.collect函数的问题的解决方案,因为我知道它是错误的编程实践,我应该找到潜在的问题源.
谢谢大家!
解决方法:
我对胜利表格不满意,但我认为这一行:
webcam_PictureBox.Image = (Bitmap)frame.Clone();
将使先前的图像不被遮挡,这会泄漏内存(Bitmap的非托管内存保留).由于Bitmap具有终结器 – 它将在未来某个时间(或当您调用GC.Collect时)由GC回收,但正如您已经了解的那样 – 在这种情况下依赖GC并不是一个好习惯.所以尝试这样做:
if (webcam_PictureBox.Image != null)
webcam_PictureBox.Image.Dispose();
webcam_PictureBox.Image = (Bitmap)frame.Clone();
Larse的合理评论:当它仍被分配给PictureBox.Image时,最好不要处理图像,因为谁知道,当你分配一个新图像时,PictureBox控件可能会对旧图像做任何事情.那么另类就是:
var oldImage = webcam_PictureBox.Image;
webcam_PictureBox.Image = (Bitmap)frame.Clone();
if (oldImage != null)
oldImage.Dispose();