zoukankan      html  css  js  c++  java
  • Finalize 与 Dispose 之间的区别

    Finalize自动释放资源,Dispose()用于手动释放资源。

    一. Finalize

      Finalize很像C++的析构函数,我们在代码中的实现形式为这与C++的析构函数在形式上完全一样,但它的调用过程却大不相同。

    ~ClassName() {//释放你的非托管资源}

      比如类A中实现了Finalize函数,在A的一个对象a被创建时(准确的说应该是构造函数被调用之前),它的指针被插入到一个 finalization链表中;在GC运行时,它将查找finalization链表中的对象指针,如果此时a已经是垃圾对象的话,它会被移入一个 freachable队列中,最后GC会调用一个高优先级线程,这个线程专门负责遍历freachable队列并调用队列中所有对象的Finalize方法,至此,对象a中的非托管资源才得到了释放(当然前提是你正确实现了它的Finalize方法),而a所占用的内存资源则必需等到下一次GC才能得到释放,所以一个实现了Finalize方法的对象必需等两次GC才能被完全释放。

      由于Finalize是由GC负责调用,所以可以说是一种自动的释放方式。但是这里面要注意两个问题:第一,由于无法确定GC何时会运作,因此可能很长的一段时间里对象的资源都没有得到释放,这对于一些关键资源而言是非常要命的。第二,由于负责调用Finalize的线程并不保证各个对象的 Finalize的调用顺序,这可能会带来微妙的依赖性问题。如果你在对象a的Finalize中引用了对象b,而a和b两者都实现了Finalize,那么如果b的Finalize先被调用的话,随后在调用a的Finalize时就会出现问题,因为它引用了一个已经被释放的资源。因此,在 Finalize方法中应该尽量避免引用其他实现了Finalize方法的对象。

      可见,这种“自动”释放资源的方法并不能满足我们的需要,因为我们不能显示的调用它(只能由GC调用),而且会产生依赖型问题。我们需要更准确的控制资源的释放。

    二. Dispose

      Dispose是提供给我们显示调用的方法。由于对Dispose的实现很容易出现问题,所以在一些书籍上(如《Effective C#》和《Applied Microsoft.Net Framework Programming》)给出了一个特定的实现模式:

    class DisposePattern :IDisposable
    
        {
    
            private System.IO.FileStream fs = new System.IO.FileStream("test.txt", System.IO.FileMode.Create);
    
            ~DisposePattern()
    
            {
    
                Dispose(false);
    
            }      
    
            IDisposable Members#region IDisposable Members
    
            public void Dispose()
    
            {
    
                //告诉GC不需要再调用Finalize方法,
    
                //因为资源已经被显示清理
    
                GC.SuppressFinalize(this);
    
                Dispose(true);
    
            }
    
            #endregion
    
                    protected virtual void Dispose(bool disposing)
    
            {
    
                //由于Dispose方法可能被多线程调用,
    
                //所以加锁以确保线程安全
    
                lock (this)
    
                {
    
                    if (disposing)
    
                    {
    
                        //说明对象的Finalize方法并没有被执行,
    
                        //在这里可以安全的引用其他实现了Finalize方法的对象
    
                    }
    
                    if (fs != null)
    
                    {
    
                        fs.Dispose();
    
                        fs = null; //标识资源已经清理,避免多次释放
    
                    }
    
                }
    
            }
    
        }

    在注释中已经有了比较清楚的描述,另外还有一点需要说明:如果DisposePattern类是派生自基类B,而B是一个实现了Dispose的类,那么DisposePattern中只需要override基类B的带参的Dispose方法即可,而不需要重写无参的Dispose和 Finalize方法,此时Dispose的实现为:

    class DerivedClass : DisposePattern
    
        {
    
            protected override void Dispose(bool disposing)
    
            {
    
                lock (this)
    
                {
    
                    try
    
                    {
    
                        //清理自己的非托管资源,
    
                        //实现模式与DisposePattern相同
    
                    }
    
                    finally
    
                    {
    
                        base.Dispose(disposing);
    
                    }
    
                }
    
            }
    
        }

    当然,如果DerivedClass本身没有什么资源需要清理,那么就不需要重写Dispose方法了,正如我们平时做的一些对话框,虽然都是继承于System.Windows.Forms.Form,但我们常常不需要去重写基类Form的Dispose方法,因为本身没有什么非托管的咚咚需要释放。

    了解GC的脾性在很多时候是非常必要的,起码在出现资源泄漏问题的时候你不至于手足无措。我写过一个生成excel报表的控件,其中对excel对象的释放就让我忙活了一阵。如果你做过excel开发的话,可能也遇到过结束excel进程之类的问题,特别是包装成一个供别人调用的库时,何时释放 excel对象以确保进程结束是一个关键问题。当然,GC的内部机制非常复杂,还有许多内容可挖,但了解所有细节的成本太高,只需了解基础,够用就好。

    ·    using() 语法有用吗?什么是IDisposable?它是如何实现确定性终结的。

    using()能自动调用Dispose方法

    比如:using()会自动调用MyObject的Dispose方法

    using ( MyObject myObject = new MyObject ( ) )
    
    {
    
       Console.WriteLine ( "quit" ) ;
    
    }

    IDisposiable是显示释放对象的接口,实现IDisposiable接口的类,可以显示的释放对象。

    通过编写Dispose方法来实现显式释放资源;

    // C#
    
    class MyClass : IDisposable
    
    {
    
    public MyClass() {} // 构造函数
    
    ~MyClass() {} // 析构方法 (不确定的) (编译器通过重载virtual void Finalize来实现),与C++/CLI的!MyClass()等效
    
    public void Dispose() {} // Dispose方法
    
    public static void Test()
    
    {
    
    using(MyClass auto = new MyClass())
    
    { /* 使用auto对象 */ }
    
    // 因为使用了using句法,编译器自动调用auto.Dispose()
    
    // 以上代码等效于:
    
    MyClass user = new MyClass();
    
    try { /* 使用user对象 */ }
    
    finally { user.Dispose(); }
    
    }
    
    }
  • 相关阅读:
    IE和FireFox兼容问题(一)zt
    FireFox不支持.style.cssText
    Firefox和IE下的弹出窗口
    内存不能为"written"错误&数据保护功能
    1~n's permutation
    pl/sqlcoalesce/nvl
    【TODO】JavaScript Demo
    pl/sqlexecute immediate usage [transferred]
    some coments of struts 1
    pl/sqldecode/case/continue
  • 原文地址:https://www.cnblogs.com/zoro-zero/p/13490336.html
Copyright © 2011-2022 走看看