• 第四节:对托管资源使用终结器


    重要提示:有的人可能有这样的心态,永远不要对托管资源使用终结器,我在很大程度上赞成这个观点,所以可以完全跳过本节,对托管资源使用终结器,是非常高的编码方式,只有极少数情况下才应该使用,要是使用必须对Finalize方法中的调用的代码有一个全面和深刻的认识。另外,还必须保证调用的代码的行为在未来的版本中不会发生改变。具体的说,Finalize方法中调用的任何代码都不能使用其他任何可能已终结的对象。

    虽然终结操作是专门来释放本地资源,但偶尔也用于托管资源,下面这个类造成计算机在垃圾回收器每执行一次回收就发出响铃声。

      sealed class GCBeep

        {

            ~GCBeep()

            {

                if (!AppDomain.CurrentDomain.IsFinalizingForUnload() && !Environment.HasShutdownStarted)

                    new GCBeep();

            }

    }

    为了使用这个类,只需要构建该类的一个实例。然后,每次执行垃圾回收,都会调用对象的Finalize方法,该方法会调用Beep并构造一个新的GCBeep对象。下次垃圾回收时,这个新的GCBeep对象的Finalize方法将得到调用。如此反复,下面程序演示该操作:

            static void Main(string[] args)

            {

                new GCBeep();

                for (Int32 x = 0; x < 100000000; x++)

                {

                    Console.WriteLine(x);

                    Byte[] b = new Byte[100];

                }

            }

    另外注意的是,即使类的实例构造跑出了异常,类型的Finalize方法也会被调用。因此,你的Finalize方法不应假定对象处于良好、一致的状态。下面的代码演示了这一点。

      sealed class TempFile

        {

            private String   m_filename = null;

            private FileStream   m_fs;

            public TempFile(string filename)

            {

                //下面这行代码可能抛出异常

                m_fs = new FileStream(filename, FileMode.Create);

                m_filename = filename;

            }

            ~TempFile()

            {

                //正确的做法是测试filename是否为空,不能保证filename已经在构造中初始化。

                if (m_filename != null) File.Delete(m_filename);

            }

        }

    另外一种做法,

      sealed class TempFile

        {

            private String   m_filename = null;

            private FileStream   m_fs;

            public TempFile(string filename)

            {

                try

                {

                    //下面这行代码可能抛出异常

                    m_fs = new FileStream(filename, FileMode.Create);

                    m_filename = filename;

                }

                catch

                {

                    GC.SuppressFinalize(this);//告诉GC不要调用Finalize方法

                    throw;//让调用者知道错误出现

                }

            }

            ~TempFile()

            {

             //这里不用判断,因为只有在构造成功之后才执行这里

                File.Delete(m_filename);

            }

    }

    设计一个类型时,出于以下几个性能方面的原因,最好是避免使用Finalize方法。

    1. 可终结的对象需要花更长的时间来分配,因为指向他们的指针必须放到终结者列表中。
    2. 可终结的对象会提升到比较老的一代,这会增大内存压力 ,并在垃圾回收器判定对象为垃圾时,阻止回收对象的内存。除此之外,该对象直接或间接引用的所有对象也会被提升。
    3. 可终结的对象导致应用程序速度变慢,这是因为每个对象在回收时,必须对他进行额外的处理。

    除此之外,注意无法控制Finalize方法在什么时候运行,Finalize方法在垃圾回收器发生时运行,而垃圾回收期可能在应用程序请求更多的内存时发生。另外CLR不保证每个Finalize方法的调用顺序。因此,在写一个Finalize方法时,应避免访问定义了Finalize方法的其它类型的对象,那些对象可能已经终结,然而,可以完全放心的访问值类型的实例,或者访问没有定义Finalize方法的引用类型的实例。调用静态方法也得小心。

    因为这些方法可能在内部访问以终结的对象,导致静态方法的行为变得无法预测。

  • 相关阅读:
    【BZOJ】【1662】/【POJ】【3252】 【USACO 2006 Nov】Round Number
    【BZOJ】【1026】【SCOI2009】Windy数
    【HDOJ】【3555】Bomb
    【HDOJ】【2089】不要62
    【Ural】【1057】Amount of degrees
    【POJ】【3710】Christmas Game
    【BZOJ】【2940】【POI2000】条纹
    【POJ】【3537】Crosses and Crosses
    【POJ】【2068】Nim
    【POJ】【2960】S-Nim
  • 原文地址:https://www.cnblogs.com/bingbinggui/p/4391090.html
Copyright © 2020-2023  润新知