Effective C#之16:Minimize Garbage

减少垃圾回收
本文探讨了减少垃圾回收压力的方法,包括提升局部变量为成员变量、利用静态成员存储常用实例及使用StringBuilder构建字符串等技巧。
rel="File-List" href="file:///C:%5CDOCUME%7E1%5CHelios%5CLOCALS%7E1%5CTemp%5Cmsohtmlclip1%5C01%5Cclip_filelist.xml"> rel="themeData" href="file:///C:%5CDOCUME%7E1%5CHelios%5CLOCALS%7E1%5CTemp%5Cmsohtmlclip1%5C01%5Cclip_themedata.thmx"> rel="colorSchemeMapping" href="file:///C:%5CDOCUME%7E1%5CHelios%5CLOCALS%7E1%5CTemp%5Cmsohtmlclip1%5C01%5Cclip_colorschememapping.xml">

Item 16: Minimize Garbage

使垃圾最少

The Garbage Collector does an excellent job of managing memory for you, and it removes unused objects in a very efficient manner. But no matter how you look at it, allocating and destroying a heap-based object takes more processor time than not allocating and not destroying a heap-based object. You can introduce serious performance drains on your program by creating an excessive number of reference objects that are local to your methods.

垃圾收集器在管理内存方面为你做了卓越的工作,它以非常高效的方式移除未使用的对象。无论你如何看待它,分配和销毁基于堆的对象,都会比不做这个工作花费更多的处理器时间。创建过多的方法内的局部引用对象,会给程序引入严重的性能负担。

So don't overwork the Garbage Collector. You can follow some simple techniques to minimize the amount of work that the Garbage Collector needs to do on your program's behalf. All reference types, even local variables, are allocated on the heap. Every local variable of a reference type becomes garbage as soon as that function exits. One very common bad practice is to allocate GDI objects in a Windows paint handler:

因此,不要过分依赖于垃圾收集器。遵循一些简单的技术,可以减少垃圾收集器要做的工作数量,这对程序是有益的。所有的引用类型,甚至局部变量,都是在堆上进行分配的。每个引用类型的局部变量,在它所在的方法退出时,立即变成垃圾。一个常见的糟糕的实践是在Windows绘画句柄内分配GDI对象。

  1.     protected override void OnPaint(PaintEventArgs e)
  2.     {
  3.         // Bad. Created the same font every paint event.
  4.         using (Font MyFont = new Font("Arial", 10.0f))
  5.         {
  6.             e.Graphics.DrawString(DateTime.Now.ToString(),
  7.               MyFont, Brushes.Black, new PointF(0, 0));
  8.         }
  9.         base.OnPaint(e);
  10.     }

OnPaint() gets called frequently. Every time it gets called, you create another Font object that contains the exact same settings. The Garbage Collector needs to clean those up for you every time. That's incredibly inefficient.

OnPain()会被频繁的调用。每次被调用的时候,都会创建另一个设置完全一样的Font对象,垃圾收集器每次都要为你进行清理工作,效率低的难以置信。

Instead, promote the Font object from a local variable to a member variable. Reuse the same font each time you paint the window:

相反,将Font对象从局部变量提升为成员变量。每次绘制窗体的时候重用同样的字体:

 

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

Your program no longer creates garbage with every paint event. The Garbage Collector does less work. Your program runs just a little faster. When you elevate a local variable, such as a font, that implements IDisposable to a member variable, you need to implement IDisposable in your class. Item 18 explains how to properly do just that.

你的程序就不会在每次绘制事件发生的时候都产生垃圾了,垃圾收集器做更少的工作,程序能运行的快一点。当你将局部变量提升为成员变量时,例如实现了IDisposablefont,就需要在你的类里面实现IDisposableItem 18解释了如何恰当的做这个工作。

You should promote local variables to member variables when they are reference types (value types don't matter) and they will be used in routines that are called very frequently. The font in the paint routine makes an excellent example. Only local variables in routines that are frequently accessed are good candidates. Infrequently called routines are not. You're trying to avoid creating the same objects repeatedly, not turn every local variable into a member variable.

当局部变量是引用类型时(值类型无所谓),同时,会被用在了被非常频繁调用的子程序里,就应该将其提升为成员变量。pain程序里面的font是个很好的例子。只有在频繁被访问的子程序里面的局部变量才是上佳的候选者。调用不频繁的子程序不是。你正在做的是避免重复性的创建同样的对象,而不是将每个局部变量改成成员变量。

The static property Brushes.Black, used earlier illustrates another technique that you should use to avoid repeatedly allocating similar objects. Create static member variables for commonly used instances of the reference types you need. Consider the black brush used earlier as an example. Every time you need to draw something in your window using the color black, you need a black brush. If you allocate a new one every time you draw anything, you create and destroy a huge number of black brushes during the course of a program. The first approach of creating a black brush as a member of each of your types helps, but it doesn't go far enough. Programs might create dozens of windows and controls, and would create dozens of black brushes. The .NET Framework designers anticipated this and created a single black brush for you to reuse whenever you need it. The Brushes class contains a number of static Brush objects, each with a different common color. Internally, the Brushes class uses a lazy evaluation algorithm to create only those brushes you request. A simplified implementation looks like this:

前面使用的静态属性Brushes.Black,举例说明了你应该使用的另外一个技术,来避免重复性的分配相似的对象。为你需要的引用类型的通用实例创建静态成员变量。考虑前面例子里面使用的黑色笔刷,每次需要在窗体上使用黑色来绘制一些东西的时候,就需要一个黑色画刷。如果每次在你画一些东西的时候都分配一个新的对象,就需要在程序的过程中,创建并销毁大量的黑色画刷,第一个方法:在每个类型里面创建一个黑色画刷作为类成员,是有用的,但是并不够。程序可能创建很多窗体和控件,也就可能创建很多的黑色画刷。.Net框架的设计者预料到了这些,为你创建了一个单独的黑色画刷,在你需要的时候进行重用。Brushes类包含很多静态的Brush对象,每个都包含了不同的常用颜色。从内部来说,Brushes使用了懒惰算法(“尽可能晚的赋值”)仅仅创建你需要的画刷。简单的实现看起来是这个样子的。

 

  1.    private static Brush blackBrush;
  2.     public static Brush Black
  3.     {
  4.         get
  5.         {
  6.            if (blackBrush == null)
  7.                 blackBrush = new SolidBrush(Color.Black);
  8.             return blackBrush;
  9.         }
  10.     }

The first time you request a black brush, the Brushes class creates it. The Brushes class keeps a reference to the single black brush and returns that same handle whenever you request it again. The end result is that you create one black brush and reuse it forevermore. Furthermore, if your application does not need a particular resource——say, the lime green brush——it never gets created. The framework provides a way to limit the objects created to the minimum set you need to accomplish your goals. Copy that technique in your programs.

第一次请求一个黑色画刷的时候,Brushes类创建它,维持一个对该单独画刷的引用,当你再进行请求的时候,就会返回同样的句柄。最终的结果就是,创建了一个黑色画刷,永远都在重用它。进一步说,如果你的应用程序不需要特殊的资源——像,莱绿色画刷——从不会被调用。框架提供了一个方式来限制对象被创建到你完成目标所需要的最小的集合中。在你的程序里面使用该技术。

You've learned two techniques to minimize the number of allocations your program performs as it goes about its business. You can promote often-used local variables to member variables. You can provide a class that stores singleton objects that represent common instances of a given type. The last technique involves building the final value for immutable types. The System.String class is immutable: After you construct a string, the contents of that string cannot be modified. Whenever you write code that appears to modify the contents of a string, you are actually creating a new string object and leaving the old string object as garbage. This seemingly innocent practice:

在你的程序完成它的功能时,你已经学会了2个技术,使程序执行最少数量的(内存、资源)分配。将经常使用的局部变量提升为成员变量。提供一个类来存储单件对象,表示一个指定类型的通用实例。最后一个技术涉及到为不可变类型构建最终值。System.String类就是不可变的:在你构建一个字符串之后,它的内容不能被修改。无论何时你编写看起来修改字符串内容的代码时,实际上,都是创建了一个新的字符串对象,原来的字符串对象就成了垃圾。这是一个表面清白的例子:

  1. string msg = "Hello, ";
  2. msg += thisUser.Name;
  3. msg += ". Today is ";
  4. msg += System.DateTime.Now.ToString();

is just as inefficient as if you had written this:

就和你这样写是一样的;

  1.     string msg = "Hello, ";
  2.     // Not legal, for illustration only:
  3.     string tmp1 = new String( msg + thisUser.Name );
  4.     string msg = tmp1; // "Hello " is garbage.
  5.     string tmp2 = new String( msg + ". Today is " );
  6.     msg = tmp2; // "Hello <user>" is garbage.
  7.     string tmp3 = new String( msg + DateTime.Now.ToString( ) );
  8.     msg = tmp3;// "Hello <user>. Today is " is garbage.

The strings tmp1, tmp2, and tmp3, and the originally constructed msg ("Hello"), are all garbage. The += method on the string class creates a new string object and returns that string. It does not modify the existing string by concatenating the characters to the original storage. For simple constructs such as the previous one, you should use the string. Format() method:

tmp1tmp2tmp3,还有原来的msg(“Hello”),全是垃圾。字符串上的+=方法创建新的字符串对象并且返回该字符串。它并不通过将字符串联到原来的存储空间上来修改已经存在的字符串。向前面这种简单的构建,你应该使用string. Format()方法:

  1.     string msg = string.Format("Hello, {0}. Today is {1}",
  2.       thisUser.Name, DateTime.Now.ToString());

For more complicated string operations, you can use the StringBuilder class:

对于更复杂的字符串操作,你可以使用StringBuilder类:

  1.     StringBuilder msg = new StringBuilder( "Hello, " );
  2.     msg.Append( thisUser.Name );
  3.     msg.Append( ". Today is " );
  4.     msg.Append( DateTime.Now.ToString());
  5.     string finalMsg = msg.ToString();

StringBuilder is the mutable string class used to build an immutable string object. It provides facilities for mutable strings that let you create and modify text data before you construct an immutable string object. Use StringBuilder to create the final version of a string object. More important, learn from that design idiom. When your designs call for immutable types (see Item 7), consider creating builder objects to facilitate the multiphase construction of the final object. That provides a way for users of your class to construct an object in steps, yet maintain the immutability of your type.

StringBuilder是可变的字符串类,可以用来构建一个不可变的字符串对象。在你构建一个不可变的字符串对象之前,它为可变字符串提供了方便,可以让你创建和修改文本数据。使用StringBuilder来创建最终版本的字符串对象。更重要的是,从这个设计习惯可以学习很多。当你的设计需要不可变类型时(Item 7),考虑,通过创建builder对象来为最终对象的多相构建提供方便。这为你的类型的用户提供了分步创建对象的方法,同时为你的类型保持了不可变性。

The Garbage Collector does an efficient job of managing the memory that your application uses. But remember that creating and destroying heap objects still takes time. Avoid creating excessive objects; don't create what you don't need. Also avoid creating multiple objects of reference types in local functions. Instead, consider promoting local variables to member variables, or create static objects of the most common instances of your types. Finally, consider creating mutable builder classes for immutable types.

垃圾收集器为你的应用程序在内存管理方面做了高效的工作。但是记住,创建和销毁堆对象仍然需要时间。避免创建额外的对象,不要创建不需要的对象。同时避免在局部方法里面,创建多个引用类型的对象。相反,考虑将局部变量提升为成员变量,或者为你的类型的最通用的实例创建静态对象。最后,考虑为不可变类型创建可变的builder类。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值