zoukankan      html  css  js  c++  java
  • 避免创建非必要的对象

    .NET的垃圾收集器(GC)让我们从繁杂的内存管理工作中解脱出来。它可以很好的管理内存,也会以一种非常高效的方式来移除内存对象中的垃圾对象。但是,尽管高效,如果我们的代码写的不够良好,不仅分配对象需要花费时间,GC帮我们销毁垃圾对象也是需要花费时间的。因此,我们应该通过一些方法来避免这些花费。

    1)将被频繁调用的引用类型的局部变量提升为成员变量

    举个例子,窗体的Paint处理函数中创建GDI对象

            protected override void OnPaint(PaintEventArgs eventArgs)
            {
                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被频繁的调用,那么,每次调用的时候,我们都必须创建一个myFont对象,而这个对象事实上是不变的都是10号的Arial字体,因此,我们可以通过以下方式来改进:
     
            private readonly Font myFont = new Font("Arial", 10.0f);
    
            protected override void OnPaint(PaintEventArgs eventArgs)
            {
                e.Graphics.DrawString(DateTime.Now.ToString(), myFont, Brushes.Black, new PointF(0, 0));
                base.OnPaint(e);
            }

    也就是,我们将myFont从局部变量提升为成员变量,这样,就不用每次调用的时候都去创建这样一个对象,也减少了GC每次调用OnPaint后都要回收一次。

    另外,我们还应该避免写出这样的代码:

    for (int i = 0; i < 10; i++)
                {
                    object obj = new object();
                    // do something
                }
    

    也就是避免在循环里面创建对象

     

    Tips:

    若某个引用类型(值类型则无所谓)的局部变量用于将被频繁调用的例程中,那么应将其提升为成员变量。当然,只有会被频繁调用时才有这个必要。

     

    2) 通过静态成员变量或单例可以让引用类型在类的各个实例中共享

    上个例子中的Brushes.Black就是这样的一个例子。如果我们每次需要一个黑色笔刷时,就去创建一个,那么在程序的执行过程中,我们就会需要创建和销毁大量的黑色笔刷。如果按照上面的方法,将黑色笔刷提升为成员变量,也是有所帮助的,但是,我们在程序的其他地方也是需要这样的黑色笔刷的。

    另外一种方法是单例,假设在一个电子商务网站中,我们创建了一个打折的规则,我们知道,这个规则的内容对于所有的地方都是一样的,因此,我们就可以将这个规则写成一个单例,这样,程序中需要用到的时候都是共享一个实例,不用每个地方/线程都去创建一个这样的规则。

     

    3)直接创建不可变类型的最终值

    以string类型为例子吧。我们知道,如果我们将2个string类型的变量进行拼接,事实上,我们会产生第三个string类型,他是前面2个拼接后的结果。也就是说string类型是不可变得。

    如:

    string msg = "Hello,";
    msg+=User.Name;
    msg+="Welcome to China."

    尽管我们这样写看上去很简单很高效,但是,上面的代码等价于:

    string msg = "Hello,";
    string temp1 = new string(msg+User.Name);
    msg = temp1;
    string temp2 = new string(msg + "Welcome to China.");
    msg = temp2;

    当这段代码执行完后,msg,temp1都将成为垃圾呗回收.一种替代的做法是使用string.Format();

    string msg = string.Format(“Hello,{0}Welcome to China.”,User.Name);

     

    我们来对比以下2段代码吧:

    string test = "Now is :" + DateTime.Now + ". Welcome!";

    vs

    string test = string.Format("Now is :{0}.Welcome",DateTime.Now);

    通过Reflector工具,我们来看看这2段代码生成的IL代码:

    image

    image

    在第一个图中,我们看到这段代码的执行时这样子的:

    1. 创建字符串”Now is:”(ldstr “Now is”)

    2. 获取DateTime.Now()并装箱

    3. 创建字符串 “.Welcome”(ldstr “.Welcome”)

    4. 将上述三部分连接起来(Stirng.Concat(object,object,object))

    而在第二个图中,我们只找到了一个ldstr,这样就为GC减轻了工作量。

    总之,GC可以高效管理应用程序使用内存,但是创建和回收堆上的对象还是需要一定的时间的,因此,我们应该尽量避免没有必要的浪费。

    参考:《Effective C#》

  • 相关阅读:
    postgresql 的统计信息
    postgresql 查看表、列的备注信息
    redis 4.0.9 cluster + startup stop
    redis 4.0.9 cluster + failover
    oracle ebs r12 打补丁的步骤
    centos 7.4 + redis 4.0.9 cluster + make
    pgpool running mode
    pgpool + streaming replication mode + slave down up
    pgpool 的安装之一
    postgresql 函数的三个状态
  • 原文地址:https://www.cnblogs.com/tian2010/p/2497135.html
Copyright © 2011-2022 走看看