Effective C# 原则16:垃圾最小化
Item 16: Minimize Garbage

垃圾回收器对内存管理表现的非常出色,并且它以非常高效的方法移除不再使用的对象。但不管你怎样看它,申请和释放一个基于堆内存的对象总比申请和释放一个不基于堆内存的对象要花上更多的处理器时间。你可以给出一些严重的性能问题,例如应用程序在某个方法内分配过量的引用对象。

你不应该让垃圾回收器超负荷的工作,为了程序的效率,你可以使用一些简单的技巧来减少垃圾回收器的工作。所有的引用类型,即使是局部变量,都是在堆上分配的。所有引用类型的局部变量在函数退出后马上成为垃圾,一个最常见的“垃圾”做法就是申请一个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;
  }
}

当你第一次申请黑色画刷时,Brushes类就会创建它。然而Brushes类就保留一个单一的黑色画刷的引用句柄,当你再次申请时它就直接返回这个句柄。结果就是你只创建了一个黑色画刷并且一直在重用它。另外,如果你的应用程序不须要一个特殊的资源,一个柠檬绿(lime green)的画刷就可能永远不会创建。框架提供了一个方法来限制对象,使得在满足目标的情况下使用最小的对象集合。学会在你的应用程序里使用这样的技巧。

你已经学会了两种技术来最小化应用程序的(对象)分配数量,正如它承担它自己的任务一样。你可以把一个经常使用的局部变量提升为类的成员变量,你可以提供一个类以单件模式来存储一些常用的给定对象的实例。最后一项技术还包括创建恒定类型的最终使用值。System.String类就是一个恒定类型,在你创建一个字符串后,它的内容就不能更改了。当你编写代码来修改这些串的内容时,你实际上是创建了新的对象,并且让旧的串成为了垃圾。这看上去是清白的例子:

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

这实际上低效的如果你是这样写:

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

字符串tmp1,tmp2,tmp3以及最原始的msg构造的(“Hello”),都成了垃圾。+=方法在字符串类上会生成一个新的对象并返回它。它不会通过把字符链接到原来的存储空间上来修改结果。对于先前这个例子,给一个简单的构造例子,你应该使用string.Format()方法:

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

对于更多的复杂的字符串操作,你应该使用StringBuilter类:

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

StringBuilder也一个(内容)可变的字符串类,用于生成恒定的字符串对象。在你还没有创建一个恒定的字符串对象前,它提供了一个有效的方法来存储可变的字符串。更重要的是,学习这样的设计习惯。当你的设计提倡使用恒定类型时(参见原则7),对于一些要经过多次构造后才能最终得到的对象,可以考虑使用一些对象生成器来简化对象的创建。它提供了一个方法让你的用户来逐步的创建(你设计的)恒定类型,也用于维护这个类型。
(译注:请理解作者的意图,只有当你使用恒定类型时才这样,如果是引用类型,就不一定非要使用对象生成器了。而且注意恒定类型的特点,就是一但创建就永远不能改变,所有的修改都会产生新的实例,string就是一个典型的例子,它是一个恒定的引用类型;还有DateTime也是一个,它是一个恒定的值类型。)

垃圾回收器在管理应用程序的内存上确实很高效。但请记住,创建和释放堆对象还是很占时间的。避免创建大量的对象,也不要创建你不使用的对象。也要避免在局部函数上多次创建引用对象。相反,把局部变量提供为类型成员变量,或者把你最常用的对象实例创建为静态对象。最后,考虑使用可变对象创建器来构造恒定对象。
======================

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:

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() 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.

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

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 );
}

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.

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.

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:

private static Brush _blackBrush;
public static Brush Black
{
  get
  {
    if ( _blackBrush == null )
      _blackBrush = new SolidBrush( Color.Black );
      return _blackBrush;
  }
}

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 resourcesay, the lime green brushit 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..

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:

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

is just as inefficient as if you had written this:

string msg = "Hello, ";
// Not legal, for illustration only:
string tmp1 = new String( msg + thisUser.Name );
string msg = tmp1; // "Hello " is garbage.
string tmp2 = new String( msg + ". Today is " );
msg = tmp2; // "Hello <user>" is garbage.
string tmp3 = new String( msg + DateTime.Now.ToString( ) );
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:

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

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

StringBuilder msg = new StringBuilder( "Hello, " );
msg.Append( thisUser.Name );
msg.Append( ". Today is " );
msg.Append( DateTime.Now.ToString());
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.

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.

转载于:https://www.cnblogs.com/WuCountry/archive/2007/03/05/664674.html

Effective C# 原则16:垃圾最小化(译)相关推荐

  1. 考虑器件匹配和寄生最小化的共质心电容器布局生成

    摘要 2013 在模拟布局设计中,电容比的精度与比例电容之间的匹配特性和互连线引起的寄生效应密切相关. 然而,之前的大部分工作只强调了共质心布局的匹配特性,而忽略了布线后的感应寄生效应. 本文解决了在 ...

  2. Effective C# 原则13:用静态构造函数初始化类的静态成员(译)

    Effective C# 原则13:用静态构造函数初始化类的静态成员 Item 13: Initialize Static Class Members with Static Constructors ...

  3. Effective C# 原则18:实现标准的处理(Dispose)模式(译)

    Effective C# 原则18:实现标准的处理(Dispose)模式 我们已经讨论过,处理一个占用了非托管资源对象是很重要的.现在是时候来讨论如何写代码来管理这些类占用的非内存资源了.一个标准的模 ...

  4. Ubuntu 16.04 和 Ubuntu 18.04 启用 点击Launcher图标,窗口实现最小化 功能

    安装了Ubuntu之后,要是每次都点击最小化按钮来实现窗口的最小化,操作起来很不方便,那么怎样才能方便操作呢, Ubuntu 16.04 本身支持 点击应用程序Launcher图标实现最小化 功能,只 ...

  5. linux权限最小化分级,vim可视化Linux系统安全最小化原则 su sudo

    一.vim在可视化模式下编辑 crl+v,会变成-- VISUAL BLOCK --,然后用上下左右键去选中. 多行注释: ESC进入命令行模式; Ctrl+v进入VISUAL BLOCK模式 上下左 ...

  6. js最小化浏览器_「译」解析、抽象语法树(ast) +如何最小化解析时间的5个技巧...

    前言 该系列课程会在本周陆续更新完毕,主要讲解的都是工作中可能会遇到的真实开发中比较重要的问题以及相应的解决方法.通过本系列的课程学习,希望能对你日常的工作带来些许变化.当然,欢迎大家关注我,我将持续 ...

  7. mysql权限最小化规则_mysql授权远程用户连接(权限最小化原则)

    1.进入mysql,创建一个新用户root,密码为root: 格式:grant 权限 on 数据库名.表名 to 用户@登录主机 identified by "用户密码"; gra ...

  8. Effective C# 原则50:了解ECMA标准(译)

    Effective C# 原则50:了解ECMA标准 Item 50: Learn About the ECMA Standard ECMA标准是C#语言所有功能的官方说明.ECMA-334定义了C# ...

  9. Effective C# 原则34:创建大容量的Web API(译)

    Effective C# 原则34:创建大容量的Web API Item 34: Create Large-Grain Web APIs 交互协议的开销与麻烦就是对数据媒体的如何使用.在交互过程中可能 ...

最新文章

  1. 邻接表存储(链式前向星)
  2. 谷歌大脑新算法,不折腾TPU就能加快AI训练速度
  3. linux进程--fork详解(三)
  4. golang执行linux命令
  5. Servlet 实例
  6. 解決memcache 有時無法連接的問題
  7. 计算机机等级考试四级模拟,《全国计算机等级考试上机考试模拟考场-四级》.pdf...
  8. matlab中inf函数,matlab中voronoin()函数的用法,求高手指点
  9. 万年历升级版 Calendar
  10. myeclipse 怎么安装与激活
  11. 配置IIS服务器,支持sis下载
  12. 突然想起今天的博客汇报没写
  13. 新手程序员必学的代码编程技巧
  14. python 串口接收多线程与界面通信_Python之PyQT多线程串口代码分析
  15. 【ELM分类】基于matlab鲸鱼算法优化核极限学习机数据分类【含Matlab源码 2012期】
  16. 群晖 映射 服务器,如何把群晖共享目录映射到Windows系统上
  17. 【转载】IntelliJ IDEA 完美破解(2099年到期)
  18. python实现连环阵
  19. 网易手游《镇魔曲》怎么打?华为畅享6S帮你春节同学聚会露一手
  20. Calendar加減月份、年份-月底的处理逻辑

热门文章

  1. TongJI Online Judge预赛(2): LOVE LETTER
  2. 在原有Android项目中快速集成React Native
  3. JS无法获取display为none的隐藏元素的宽度和高度的解决方案
  4. Tensorflow 处理libsvm格式数据生成TFRecord (parse libsvm data to TFRecord)
  5. android 自由复制与粘贴功能
  6. 那些年借“云”出海的日子
  7. 前端(二)之 CSS
  8. 关于js封装函数的一些东西
  9. css考核点整理(十一)-响应式开发经验,响应式页面的三种核心技术是什么
  10. Devexpress 重新编译以后 重新生成DEMO