我构建控制台应用程序以进行此类测试。
while (true)
{
var bmp = new Bitmap(1600, 1200);
//var buffer = new byte[2000 * 1000 * 4];
Thread.Sleep(10);
}
运行后,内存在很短的时间内增加到2GB以上。
这是Bitmap
。
[2017-04-27 12:15:03][00:00:01.0150128] PrivateBytes : 1583.0MB, AllHeapsBytes : 0.0MB, Thread Count : 23, CPU Usage : 12%
[2017-04-27 12:15:04][00:00:02.0019966] PrivateBytes : 2150.0MB, AllHeapsBytes : 0.0MB, Thread Count : 24, CPU Usage : 10%
[2017-04-27 12:15:05][00:00:03.0030021] PrivateBytes : 500.0MB, AllHeapsBytes : 4.1MB, Thread Count : 24, CPU Usage : 26%
[2017-04-27 12:15:06][00:00:04.0040047] PrivateBytes : 1043.0MB, AllHeapsBytes : 4.1MB, Thread Count : 24, CPU Usage : 9%
[2017-04-27 12:15:07][00:00:05.0050024] PrivateBytes : 1601.0MB, AllHeapsBytes : 4.1MB, Thread Count : 24, CPU Usage : 7%
[2017-04-27 12:15:08][00:00:06.0060058] PrivateBytes : 2136.0MB, AllHeapsBytes : 4.1MB, Thread Count : 24, CPU Usage : 9%
[2017-04-27 12:15:09][00:00:07.0069981] PrivateBytes : 2695.0MB, AllHeapsBytes : 4.1MB, Thread Count : 24, CPU Usage : 14%
如果我像这样更改Bitmap
到byte
数组,内存使用情况就会稳定。
while (true)
{
//var bmp = new Bitmap(1600, 1200);
var buffer = new byte[2000 * 1000 * 4];
Thread.Sleep(10);
}
这是byte
数组的情况的日志。
[2017-04-27 12:25:09][00:00:01.0080196] PrivateBytes : 63.0MB, AllHeapsBytes : 31.0MB, Thread Count : 23, CPU Usage : 11%
[2017-04-27 12:25:10][00:00:02.0020012] PrivateBytes : 66.0MB, AllHeapsBytes : 46.2MB, Thread Count : 24, CPU Usage : 18%
[2017-04-27 12:25:11][00:00:03.0030496] PrivateBytes : 67.0MB, AllHeapsBytes : 47.1MB, Thread Count : 24, CPU Usage : 8%
[2017-04-27 12:25:12][00:00:04.0040530] PrivateBytes : 67.0MB, AllHeapsBytes : 47.1MB, Thread Count : 24, CPU Usage : 10%
[2017-04-27 12:25:13][00:00:05.0050386] PrivateBytes : 67.0MB, AllHeapsBytes : 47.1MB, Thread Count : 24, CPU Usage : 11%
[2017-04-27 12:25:14][00:00:06.0060466] PrivateBytes : 52.0MB, AllHeapsBytes : 31.9MB, Thread Count : 24, CPU Usage : 10%
[2017-04-27 12:25:15][00:00:07.0070521] PrivateBytes : 67.0MB, AllHeapsBytes : 47.1MB, Thread Count : 24, CPU Usage : 18%
我知道如果我打电话给bmp.Dispose()
,它就会变成稳定的内存使用。
我很好奇为什么Bitmap
被垃圾收集得慢,为什么不byte
数组。
答案 0 :(得分:3)
Bitmap是一个名为gdiplus的非托管图形库的包装类。它是在该库的C++ wrapper之后建模的,C#程序员使它接近一对一的匹配,你可以从文档中看出来。方法和属性非常小,它们只是对库函数进行了调整。
并且类对象非常小,它没有自己的字段,并从其基类Image继承3。只需要24个字节的GC堆。在您触发垃圾收集之前,您可以创建批次,接近十万。真实存储在gdiplus库中是非托管的,与Width * Height成比例,具体取决于图像的像素格式。
仅在调用Dispose()或垃圾回收器运行终结器时才会释放非托管存储。 .NET程序员在第一次开始编程时几乎总是忽略Dispose / using。他们总是在Bitmap上遇到制造商。
项目>属性>资源在这个故事中引人注目。它提供了非常方便的语法来在程序中使用位图资源。但这很危险,几乎没有人意识到每次他们在代码中使用Properties.Resources.Somename时,他们都会得到一个必须处理的全新Bitmap对象。
这经常出错,微软决定在WPF中使用完全不同的方法。仍然是unmanaged graphics library的包装类,最近一个叫做WIC。但故意不实现IDisposable。和dispose it automatically的计划。 .NET框架代码调用GC.Collect(),ouch。但更难发现它并没有很好地发挥作用。
答案 1 :(得分:1)
Bitmap是一个可终结的对象。它具有需要正确处理的非托管资源。当您调用Dispose()
方法时,通常会处理这些资源。如果没有调用Dispose()
,则CLR需要先通过调用Bitmaps的终结器来处理资源。这是由CLR中的一个特定线程完成的,称为终结线程。这是一个需要时间的额外步骤。这就是为什么如果你不需要它,你总是需要Dispose()
位图。 using
语句会自动执行。字节数组是一个简单的托管对象。它不需要最终确定
答案 2 :(得分:0)
答案 3 :(得分:0)
你应该在使用后使用use来处理BitMap。这将很好地了解为什么不能尽快收集。
“表示位图的.NET对象只有48个字节,但关联的非托管大小可能会大得多。”
while (true)
{
using (var bmp = new Bitmap(1600, 1200))
{
//var buffer = new byte[2000 * 1000 * 4];
Thread.Sleep(10);
}
}