我正在调查大型应用程序中的GDI资源泄漏。为了进一步了解这些问题是如何发生的,我创建了一个非常小的应用程序,我故意将其“泄漏”。这是一个简单的用户控件,它应该导致创建100个Pen对象:
public partial class TestControl : UserControl { private List pens = new List(); public TestControl() { InitializeComponent(); for (int i = 0; i < 100; i++) { pens.Add(new Pen(new SolidBrush(Color.FromArgb(255, i * 2, i * 2, 255 - i * 2)))); } this.Paint += new PaintEventHandler(TestControl_Paint); } void TestControl_Paint(object sender, PaintEventArgs e) { for (int i = 0; i < 100; i++) { e.Graphics.DrawLine(pens[i], 0, i, Width, i); } } }
然而,当我创建一个我的对象的实例并将其添加到一个表单时,用TaskManager查看我的应用程序我现在看到~37个GDI对象。如果我重复向表单添加新的TestObject用户控件,我仍只能看到~37个GDI对象。
这里发生了什么!我认为System.Drawing.Pen的构造函数将使用GDI + API创建一个新的Pen,从而使用一个新的GDI对象。
我一定要疯了。如果我不能编写一个创建GDI对象的简单测试应用程序,我该如何创建一个泄漏它们的应用程序!
非常感谢任何帮助。
最诚挚的问候,科林E.
答案 0 :(得分:2)
GDI +是否使用GDI句柄?我不确定,虽然我在某处读到有一个依赖于裸GDI的.NET System.Drawing实现。
但是,也许您可以尝试使用像AQTime这样的分析器来查找泄漏。
您如何确定您的大型应用正在泄漏GDI句柄?任务管理器中的计数是大吗?如果是这样,你总是使用GDI +,还是GDI?如果您多次创建控件,您的测试应用程序GDI会处理计数增加吗?
答案 1 :(得分:2)
您的样本中并没有真正泄漏资源。从Load事件中删除此代码:
for (int i = 0; i < 100; i++)
{
pens.Add(new Pen(new SolidBrush(Color.FromArgb(255, i * 2, i * 2, 255 - i * 2))));
}
您的Paint事件处理程序应如下所示:
void TestControl_Paint(object sender, PaintEventArgs e)
{
for (int i = 0; i < 100; i++)
{
e.Graphics.DrawLine(new Pen(new SolidBrush(Color.FromArgb(255, i * 2, i * 2, 255 - i * 2))), 0, i, Width, i);
}
}
现在你将在每次油漆电话中泄漏。开始最小化/恢复你的表格,看看GDI对象天空火箭...
希望这有帮助。
答案 2 :(得分:1)
如果你想从.NET泄漏GDI对象,那么只需创建一个GDI对象而不释放它:
[DllImport("gdi32.dll", EntryPoint="CreatePen", CharSet=CharSet.Auto, SetLastError=true, ExactSpelling=true)]
private static extern IntPtr CreatePen(int fnStyle, int nWidth, int crColor);
CreatePen(0, 0, 0); //(PS_SOLID, 0=1px wide, 0=black)
Blingo blango,你正在泄漏GDI笔。
我不知道为什么你想要创建GDI泄漏。但是你的问题是如何从WinForm创建GDI泄漏 - 所以就是这样。
答案 3 :(得分:0)
我认为以下博客可能已经回答了这个问题:
Using GDI Objects the Right Way
未明确处置的GDI对象应由其finalize隐式处理。 (鲍勃鲍威尔也在GDI+ FAQ)
中提到了这个问题但我怀疑CLR垃圾收集器是否可以如此快速地删除GDI资源,甚至无法从TaskManager中看到内存使用情况的变化。也许当前的GDI +实现不使用GDI。
我尝试了以下代码来生成更多GDI对象。但我仍然看不到GDI句柄数量的任何变化。
void Form1_Paint(object sender, PaintEventArgs e)
{
Random r = new Random();
while (true)
{
for (int i = 0; i < 100; i++)
{
e.Graphics.DrawLine(
new Pen(new SolidBrush(Color.FromArgb(r.Next()))), 0, i, Width, i);
}
}
}
答案 4 :(得分:0)
我认为编译器只使用一个句柄。
如果我在delphi中创建了很多字体,我只需要记忆
但如果我使用WinAPI CreateFont()
,我会使用GDI对象。
答案 5 :(得分:0)
在表单上创建两个按钮。在每个按钮内,添加以下代码。在一个按钮中,注释掉Dispose方法。
Form _test = null;
for (int i = 0; i < 20; i++)
{
_test = new Form();
_test.Visible = false;
_test.Show();
_test.Hide();
_test.Dispose();
}
Dispose注释掉的按钮显示泄漏。另一个显示Dispose导致User和GDI句柄保持不变。
This可能是我发现的解释它的最佳页面。