如何在 Windows 窗体中创建 GDI 泄漏!
我正在调查大型应用程序中的 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.
I am investigating a GDI resource leak in a large application. In order to further my understanding of how these problems occur, I have created a very small application which I have deliberately made 'leaky'. Here is a simple user control which should result in the creation of 100 Pen objects:
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); } } }
However, when I create an instance of my object and add it to a form, looking at my application with TaskManager I currently see ~37 GDI objects. If I repeatedly add new TestObject user controls to my form, I still only see ~37 GDI objects.
What is going on here! I thought that the constructor for System.Drawing.Pen would use the GDI+ API to create a new Pen, thus using a new GDI object.
I must be going nuts here. If I cannot write a simple test application that creates GDI objects, how can I create one which leaks them!
Any help would be much appreciated.
Best Regards, Colin E.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
GDI+ 是否使用 GDI 句柄? 我不确定,尽管我在某处读到有一个依赖于裸 GDI 的 .NET System.Drawing 实现。
但是,也许您可以尝试使用 AQTime 等分析器来查找泄漏。
您如何确定您的大型应用程序正在泄漏 GDI 句柄? 任务管理器中的计数是否很大? 如果是这样,您总是使用 GDI+,还是同时使用 GDI? 如果多次创建控件,您的测试应用程序 GDI 句柄计数是否会增加?
Does the GDI+ use GDI handles? I'm not sure, though I read somewhere that there is a .NET System.Drawing implementation that relies on bare GDI.
However, maybe you can try to find your leaks with a profiler like AQTime instead.
How are you sure your large app is leaking GDI handles? Is the count in Task Manager large? If so, are you always using GDI+, or also GDI? Does your test app GDI handle count increase if you create your control multiple times?
您并没有真正泄漏样本中的资源。 从 Load 事件中删除此代码:
您的 Paint 事件处理程序应如下所示:
现在,您将在每次绘制调用中发生泄漏。 开始最小化/恢复您的窗体并查看 GDI 对象飞速上升...
希望这会有所帮助。
You are not really leaking resources in your sample. Remove this code from your Load event:
Your Paint event handler should look like this:
Now you will be leaking in every paint call. Start minimizing/restoring your Form and see GDI objects sky rocket...
Hope this helps.
如果你想从 .NET 泄漏 GDI 对象,那么只需创建一个 GDI 对象而不释放它:
Blingo blango,你正在泄漏 GDI 笔。
If you want to leak a GDI object from .NET, then just create a GDI object and not release it:
Blingo blango, you're leaking GDI pens.
我认为编译器只使用一个句柄。
如果我在 delphi 中创建很多字体,我只会占用内存
但如果我使用 WinAPI
CreateFont()
我会获取 GDI 对象。I think the compiler only use one handle.
If I in delphi create a lot of fonts I just take memory
but if I use the WinAPI
CreateFont()
I take GDI objects.在窗体上创建两个按钮。 在每个按钮内添加以下代码。 一键注释掉 Dispose 方法。
注释掉 Dispose 的按钮会显示泄漏情况。 另一个显示 Dispose 导致 User 和 GDI 句柄保持不变。
这可能是我发现的最好的页面解释了它。
Create two buttons on a form. Inside each button, add the following code. In one button, comment out the Dispose method.
The button with the Dispose commented out shows you the leak. The other shows that Dispose causes the User and GDI handles to stay the same.
This is probably the best page I've found that explains it.
我认为以下博客可能已经回答了这个问题:
以正确的方式使用 GDI 对象
未显式处置的 GDI 对象应由其终结隐式处置。
(Bob Powell 在 GDI+ FAQ< 中也提到了这个问题/a> )
但我怀疑 CLR 垃圾收集器是否可以如此快速地删除 GDI 资源,以至于我们甚至无法从 TaskManager 看到内存使用情况的变化。 也许当前的 GDI+ 实现不使用 GDI。
我尝试了以下代码来生成更多 GDI 对象。 但我仍然看不到GDI句柄数量有任何变化。
I think the following blog may have answered this question:
Using GDI Objects the Right Way
The GDI objects that aren't explicitly disposed should be implicitly disposed by their finalizes.
(Bob Powell has also mentioned this issue in GDI+ FAQ )
But I doubt if the CLR garbage collector can remove GDI resources so quickly that we can't even see memory usage changes from TaskManager. Maybe current GDI+ implementation doesn't use GDI.
I've tried the following piece of code to generate more GDI objects. But I still couldn't see any changes of the number of GDI handles.