在我们的应用程序中,我们有一些GDI +对象,这些对象经常在许多不同的上下文中使用。这包括Font
,SolidBrush
(白色,黑色......),某些Pen
...
对于那些对象,到目前为止,我们的策略是通过广泛可见的静态只读字段来保存它们。这避免了创建/处置它们数百万的时间。我们当然在这些对象上处理线程安全访问(基本上只从UI线程访问它们)。
这些GDI +对象中只有少数在应用程序的生命周期中保留,例如200.其他GDI +对象(具有短生命的对象)都被 asap 处理。但是我们有时会得到意想不到的GDI +资源中断异常,希望很少。
我想知道这些例外是否可以来自这些少数GDI +对象,如果这是创建/处置大量短期GDI +对象的更明智的策略。有没有人对这两种策略有实际经验和相关结论?
答案 0 :(得分:11)
缓存System.Drawing对象是一个错误。它们制作起来非常便宜,而且非常昂贵。它们分配在特殊堆上,桌面上的所有进程都需要共享。堆大小限制为65535个对象。与使用它们执行的操作的成本相比,创建像画笔或笔这样的对象大约需要一微秒,微不足道。唯一昂贵的绘图对象是Font。创建一个涉及字体映射器占用的大量开销。但这已经由.NET解决了,它会缓存它们。
除了不必要地占用堆外,编程风格很危险。忘记盲目地应用使用语句太容易了。这当然可以让你遇到麻烦,你依靠终结者线程再次释放它们。如果一个程序涉及大量的重绘但没有足够的对象分配来触发GC,那么你将耗尽GDI对象的配额。默认情况下,进程为10,000个对象。这会使你的程序崩溃。请注意,System.Drawing类包装器不足以自行触发GC,其中10000个不足以使终结器运行并再次释放句柄。
问题很容易诊断,您可以使用任务管理器。使用View + Select Columns并勾选“GDI Objects”。不妨勾选“用户对象”,另一个很容易泄露的用户对象。在使用过程中,请密切关注过程的显示计数。一个稳定攀升的数字意味着厄运。