当前位置 > 首页 > Asp.net

《Effective C#》读书笔记——条目16:避免创建非必要的对象<.NET资源管理>

2012-11-27 9:12:00来源:Asp.net

  我们知道:C#是一门虚拟机语言,C#编译器首先将C#代码编译成IL代码,运行程序时CLR(Common Language Runtime,公共语言运行时)通过调用JIT(just-in-time Compiler即时编译器)来将IL代动态即时编译成可执行的机器码。GC(Garbage Collector,垃圾收集器)自动为我们的应用程序进行内存管理的分配和释放,(具体参见:了解.NET 内存管理机制),以一种高效的方式来移除内存中的垃圾对象,不过不管有多高效,分配和销毁在堆上的对象总会花费掉时间。

  如果我们在一个方法中创建了过多的引用对象,会对应用程序的性能产生严重的影响。因此我们应该遵守下面的一些规则,可以尽量的降低GC的工作量。

 

阅读目录:

      1.将常用的局部变量提升为成员变量

      2.为常用的类型实例提供静态对象

      3.为不可变类型提供可变的创建对象

      小节

 

1.将常用的局部变量提升为成员变量

   所有的引用类型,包括那些局部变量,都会分配到堆上。在函数退出后,函数内的所有局部变量都会立即变成垃圾对象。所以我们可以得出结论:

若是某个引用类型(值类型无所谓)的局部变量用于被频繁调用的例程中,那么应该将其提升为成员变量。这既有助于减轻GC的负担,也可以提升程序运行的效率。

 在GUI编程中一个常见的错误就是:在窗体的Paint处理函数中创建GDI(Graphics Device Interface,图形设备接口)对象,如下:

1 protected override void OnPaint(PaintEventArgs e)
2 {
3 using (Font myFont = new Font("Arial", 10.0f))
4 {
5 e.Graphics.DrawString(DateTime.Now.ToString(), myFont, Brushes.Black, new Point(0, 0));
6 }
7 base.OnPaint(e);
8 }

 

OnPaint()将会被非常频繁的调用,每次调用都会创建一个Font对象,而包含的内容完全和上一次一样。所以GC需要每次都为你清扫这些垃圾,严重影响了应用程序的效率。其实我们完全可以将Font对象提升为成员变量,是每次窗体重绘时能够重用该Font对象:

1 private readonly Font myFont = new Font("Arial", 10.0f);
2
3 protected override void OnPaint(PaintEventArgs e)
4 {
5 e.Graphics.DrawString(DateTime.Now.ToString(), myFont, Brushes.Black, new Point(0, 0));
6
7 base.OnPaint(e);
8 }

 将常用的局部变量提升为成员变量之后,程序无需每次重绘时生成垃圾对象,减轻了GC的负担,也提升了程序的效率。不过这里有一个小小的限制:将实现了IDisposable接口的局部变量提升为成员变量,那么这个成员变量依附类本身也需要实现IDisposable接口。

 

2.为常用的类型实例提供静态对象

  静态成员变量可以让引用类型在类的各个实例中共享。我们可以通过提供了一个类,存放某个类型常用的实例的单例对象,这样可以避免创建重复的对象。.NET Framework 的类库中就有很多这样的做法:Brushes类包含了一系列的静态Brush对象,每个都包含了一种常用的颜色。它们的简要实现如下:

1 private static Brush blackBrush;
2 public static Brush Black
3 {
4 get