我正在构建一些需要图像平移,缩放和亮度控制的软件。我设置了一个小项目,在网上找到很多例子之后建立一个简单的亮度控制,所以请忽略我在主窗体中做的所有事情
当我开始使用轨道栏控制器时,当值(-100到100)发生变化时会触发下面的事件,内存使用量会以秒为单位叠加到千兆字节。然后它会坐在那里,永远不会释放记忆。再次移动轨迹栏会占用更多内存
private void trackBarBrightness_EditValueChanged(object sender, EventArgs e)
{
float value = trackBarBrightness.Value * 0.01f;
float[][] colorMatrixElements =
{
new float[] {
1,
0,
0,
0,
0
},
new float[] {
0,
1,
0,
0,
0
},
new float[] {
0,
0,
1,
0,
0
},
new float[] {
0,
0,
0,
1,
0
},
new float[] {
value,
value,
value,
0,
1
}
};
ColorMatrix colorMatrix = new ColorMatrix(colorMatrixElements);
ImageAttributes imageAttributes = new ImageAttributes();
imageAttributes.SetColorMatrix(colorMatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);
//Image img = this.pictureEdit.Image;
Image img = originalImage;
Graphics g = default(Graphics);
Bitmap bm = new Bitmap(Convert.ToInt32(img.Width), Convert.ToInt32(img.Height));
g = Graphics.FromImage(bm);
g.DrawImage(img, new Rectangle(0, 0, bm.Width, bm.Height), 0, 0, bm.Width, bm.Height, GraphicsUnit.Pixel, imageAttributes);
pictureEdit.Image = bm;
}
非常感谢任何指导
答案 0 :(得分:4)
问题是您没有在Dispose
上致电Bitmap
。 .Net中的Bitmap
本质上只是一个包含大型无人GDI +位图(source)的小型托管对象。正是这个GDI +位图存储像素数据,因此负责内存消耗。托管的.Net Bitmap
只是一个包装器,相比之下,远远小于。
GC.Collect()
释放内存的原因是因为您强制GC释放所有不再被引用的位图,这些位图反过来释放它们指向的所有非托管内存。 GC不会自动为您执行此操作的原因是因为GC仅跟踪托管内存而非跟踪非托管内存。因此,从GC的角度来看,你留下的所有这些托管位图都很小,并没有占用大量空间,因此收集它们并不重要。事实上,当它们相当大时。
来自MSDN:
如果小型托管对象分配大量非托管内存,则运行时仅考虑托管内存,因此低估了调度垃圾回收的紧迫性。
当您不再需要Dispose
时调用Bitmap
,您将立即释放GDI +位图使用的非托管内存。托管的.Net Bitmap
仍然会一直闲逛,直到GC收集它,但是谁在乎,这很小。
答案 1 :(得分:1)
Bitmap继承自Image和Image有一个终结器!这使得它在创建时自动生成Gen1,如果GC没有感觉到内存压力,可能需要几分钟才能处理和释放。
更新的快节奏实际上是创建了许多实例,并且必须由终结器线程逐个收集。有时需要15分钟,有时甚至更早。
解决方案是随时限制位图的数量,并且在任何时刻只需要一个,最新的一个,当被替换时,前一个可以被处置,当前呈现直到下次更新。
然后你应该做这样的事情:
private void trackBarBrightness_EditValueChanged(object sender, EventArgs e)
{
//your current code here up to (excluding): pictureEdit.Image = bm;
IDisposable PreviousImage = pictureEdit.Image;
pictureEdit.Image = bm;
PreviousImage?.Dispose();
}
或者,如果PictureBox是只写的:
private IDisposable PreviousImage {get;set;}
private void trackBarBrightness_EditValueChanged(object sender, EventArgs e)
{
//your current code here up to (excluding): pictureEdit.Image = bm;
pictureEdit.Image = bm;
PreviousImage?.Dispose();
PreviousImage = bm;
}
答案 2 :(得分:0)
不要看到你正在使用任何循环,所以不确定,但你可以考虑使用内存分析器,但是我看到你不处理创建的Image
对象全部。我的意思是以下两行。您应该在其上明确调用Dispose()
或使用Using(...) { ... }
构造以便更好地处理
Image img = originalImage;
Graphics g = default(Graphics);