我最近遇到了this VerticalLabel control on CodeProject。
我注意到OnPaint方法会创建但不会释放Pen和SolidBrush对象。
这是否重要,如果是这样,我怎样才能证明它可能导致的任何问题?
编辑
这不是关于IDisposable模式的问题。我知道调用者通常应该在任何实现IDisposable的类上调用Dispose。
我想知道的是,当GDI +对象没有像上面的例子那样被处理时,可以预期会出现什么问题(如果有的话)。很明显,在链接的示例中,可能会在垃圾收集器启动之前多次调用OnPaint,因此可能会耗尽句柄。
但是我怀疑GDI +在某些情况下内部重用句柄(例如,如果你使用Pens类中特定颜色的笔,它会被缓存并重用)。
我想要了解的是,链接示例中的代码是否能够在忽略调用Dispose的情况下逃脱。
如果没有,看一个样本,证明它可能导致什么问题。
我应该补充一点,我经常(including the OnPaint documentation on MSDN)看到WinForms代码示例无法处理GDI +对象。
答案 0 :(得分:5)
当然这很重要。在不再需要时,必须处理实现IDisposable的所有类实例。当类使用非托管资源(即.NET Framework未处理的操作系统资源)时,类被定义为IDisposable。
如果你没有手动处理对象,那么在垃圾收集器调用对象终结器之前不会释放这些非托管资源(这只有在类正确实现the Dispose pattern时才会发生),因为垃圾收集器只在检测到实际内存不足时运行才会很晚。因此,在不处理对象时,您将保留其他应用程序可能使用的操作系统资源。
有关此主题的讨论,请访问:http://agilology.blogspot.com/2009/01/why-dispose-is-necessary-and-other.html
答案 1 :(得分:3)
FWIW,在GDI中有“库存”对象。创建库存对象时,您没有删除它,因为它由操作系统“拥有”。
您可能已经了解了库存对象,但这里有一个link详细介绍。
我不知道GDI +中是否有类似的“库存”对象。我刚刚搜索了一下,但没有找到任何引用。
作为一个测试我写了一个小的WinForms程序,带有一个定时器回调(设置为每10毫秒触发一次),如下所示:
private void timer1_Tick(object sender, EventArgs e)
{
byte r = (byte)rnd.Next(0, 256);
byte g = (byte)rnd.Next(0, 256);
byte b = (byte)rnd.Next(0, 256);
System.Drawing.SolidBrush sb = new SolidBrush(Color.FromArgb(0,r,g,b));
}
如果我让它运行,它将慢慢消耗内存。通过观察TaskManager(不是最精确的测量方法),内存使用量往往会增加(在我的机器上,使用.NET 4.0 VS2010,Release),每个任务管理器更新大约20k字节(以最高更新速率)。如果我在画笔上调用Dispose,则每个任务管理器更新的内存使用量往往增加约8k。
几乎不是一个明确的测试,但是如果没有处理SolidBrush,它似乎指向更长的内存使用量。有趣的是,在测试运行时(无论哪种情况下),我都没有增加Handles和GDI Objects。根据过去GDI资源泄漏的经验,我预计可能会看到GDI Objects正在增长,特别是在非Dispose案例中。
无论如何,这可能是有用的,也许不是。
答案 2 :(得分:3)
我需要在应用程序中修复一些内存泄漏,所以我正在做一些调查。简而言之,似乎在大多数情况下,你可以通过略微更高的内存使用来侥幸使用它。
以下是一个病理案例。我在任务管理器(我知道的粗略)中监视我的测试应用程序,并观察内存(私有工作集),句柄,用户对象和GDI对象列。 Button1单击导致比Button2点击更多的内存使用。
如果我在Button1上快速点击并持续点击,当内存达到约1.6GB时,我可能会导致'System.OutOfMemoryException'。无论我多么疯狂或坚持不懈,Button2永远不会超过大约12MB。
我正在使用win7 64位计算机构建vs。2010 .net 4客户端配置文件winforms app。显然你通常不会建造数以百万计的刷子......
关心大卫
private void button1_Click(object sender, EventArgs e) {
for (int i = 0; i < 500000; i++) {
SolidBrush b = new SolidBrush(Color.FromArgb(2, 32, 43, 128));
}
}
private void button2_Click(object sender, EventArgs e) {
for (int i = 0; i < 500000; i++) {
using (SolidBrush b = new SolidBrush(Color.FromArgb(2, 32, 43, 128))) {
}
}
}
答案 3 :(得分:1)
对象 应该 在它们没有被其他任何东西引用之后,在它们已经超出范围之后被垃圾收集处理掉。
证明这种情况是否发生的最简单方法(不进行任何代码更改)是在表单上使用大量这些控件,并查看应用程序使用的内存是否保持增长或保持稳定。
答案 4 :(得分:1)
它只是泄漏未管理的资源(笔/画笔),直到垃圾收集器回收相应的托管对象。所以你浪费了一些之后不再使用的手柄。
这不是太大的交易,因为Finalize()
会调用Dispose()
,但释放非托管资源通常应该比较早完成。
答案 5 :(得分:0)
另外我会告诉我最好使用ready集合: System.Drawing.Pens。 和 System.Drawing.Brushes。 如果你不以特殊方式自定义它们。