垃圾回收器对内存管理表现的非常出色,并且它以非常高效的方法移除不再 使用的对象。但不管你怎样看它,申请和释放一个基于堆内存的对象总比申请和 释放一个不基于堆内存的对象要花上更多的处理器时间。你可以给出一些严重的 性能问题,例如应用程序在某个方法内分配过量的引用对象。
你不应该 让垃圾回收器超负荷的工作,为了程序的效率,你可以使用一些简单的技巧来减 少垃圾回收器的工作。所有的引用类型,即使是局部变量,都是在堆上分配的。 所有引用类型的局部变量在函数退出后马上成为垃圾,一个最常见的“垃 圾”做法就是申请一个Windows的画图句柄:
protected override void OnPaint( PaintEventArgs e )
{
// Bad. Created the same font every paint event.
using ( Font MyFont = new Font( "Arial", 10.0f ))
{
e.Graphics.DrawString( DateTime.Now.ToString(),
MyFont, Brushes.Black, new PointF( 0,0 ));
}
base.OnPaint( e );
}
OnPaint()函数的调用很频繁的,每次调用它的时候, 都会生成另一个Font对象,而实际上它是完全一样的内容。垃圾回收器每次都须 要清理这些对象。这将是难以置信的低效。
取而代之的是,把Font对象 从局部变量提供为对象成员,在每次绘制窗口时重用同样的对象:
private readonly Font _myFont =
new Font( "Arial", 10.0f );
protected override void OnPaint( PaintEventArgs e )
{
e.Graphics.DrawString( DateTime.Now.ToString( ),
_myFont, Brushes.Black, new PointF( 0,0 ));
base.OnPaint( e );
}
这样你的程序在每 次paint事件发生时不会产生垃圾,垃圾回收器的工作减少了,你的程序运行会 稍微快一点点。当你把一个实现了IDisposable接口的局部变量提升为类型成员 时,例如字体,你的类同样也应该实现IDisposable接口。原则18会给你解释如 何正确的完成它。
当一个引用类型(值类型的就无所谓了)的局部变量在 常规的函数调用中使用的非常频繁时,你应该把它提升为对象的成员。那个字体 就是一个很好的例子。只有常用的局部变量频繁访问时才是很好的候选对象,不 是频繁调用的就不必了。你应该尽可能的避免重复的创建同样的对象,使用成员 变量而不是局部变量。
前面例子中使用的静态属性Brushes.Black,演示 了另一个避免重复创建相似对象的技术。使用静态成员变量来创建一些常用的引 用类型的实例。考虑前面那个例子里使用的黑色画刷,每次当你要用黑色画刷来 画一些东西时,你要在程序中创建和释放大量的黑色画刷。前面的一个解决方案 就是在每个期望黑色画刷的类中添加一个画刷成员,但这还不够。程序可能会创 建大量的窗口和控件,这同样会创建大量的黑色画刷。.Net框架的设计者预知了 这个问题,他们为你创建一个简单的黑色画刷以便你在任何地方都可以重复使用 。Brushes对象包含一定数量的静态Brush对象,每一个具有不同的常用的颜色。 在内部,Brushes使用了惰性算法来,即只有当你使用时才创建这些对象。一个 简单的实现方法:
private static Brush _blackBrush;
public static Brush Black
{
get
{
if ( _blackBrush == null )
_blackBrush = new SolidBrush( Color.Black );
return _blackBrush;
}
}