using System;
public class A
{   ~public A()
     {
        Console.WriteLine("Destructor instance of A");
      }   public void F()
    {
       Console.WriteLine("Call A.F()");
       Test.a = this;
    }
}public class B
{
   private A a = null;   public void SetA(A a)
    {
       this.a = a; 
    }
   
   ~public B()
     {
        Console.WriteLine("Destructor instance of B");
        a.F();  
        
      }
}
public class Test
{
   public static A a = null;
   public static B b = null;   static void Main()
    {
       b = new B();
       a = new A();
       b.SetA(a);
       
       b = null;
       a = null;
       GC.Collect();
       GC.WaitForPendingFinalizers();
      
       if(a != null)
        Console.WriteLine("a is not null");
    }
}当垃圾回收器首先执行A的析构函数的时候,有以下的输出:
Destructor instance of A
Destructor instance of B
Call A.F()
当执B的析构函数时可能使本来没有被引用的A的实例现在被Test.a进行引用,致使B对象符合回收条件而A不具备回收条件。
问题:
A的析构函数不是首先被调用的吗?难道对象被析构后在其他范围内还可以被引用吗?(包括在其他析构函数中或程序入口点中)
对象在析构后不是等待垃圾回收器进行内存回收吗?难道说由于B的析构函数是的Test.a引用了A的实例,将A对象至于活动的。
所以A的析构函数执行后还可以正常使用,对A来说执行析构函数后A收到影响了吗?请您详细的解释一下!

解决方案 »

  1.   

    垃圾对象的回收由GC决定什么时候执行回收...一个对象被标记为垃圾对象后仍然正常存在于垃圾托管堆,析构函数不会被执行,这个对象仍然存活,随时可以用弱引用重新激活它...只有在GC销毁该对象时才会调用析构函数,而这个销毁时间你是无法预知的...所以析构函数什么时间执行也无法预知...这也是.NET托管代码不推荐使用析构函数的原因...
      

  2.   

    析构函数的执行是.NET的垃圾回收器来控制的
    立即销毁
    调用:GC.Collect(); 
    实现IDisposable 接口的Dispose方法
      

  3.   

    世界又美好了:GC的出现析构函数只在GC开始释放对象所占用的内存时调用而不是设置对象null时执行 设置对象null只是段开了内存对象与引用句柄之间的引用关系而已但如果你想使用完对象后调用析构函数该怎么办?1、调用GC来清理2、实现IDisposable接口
      

  4.   

    @VR:
    @WU:
    @MZ:
    你们讲的都挺明白,但是我想问在A执行玩析构函数后,执行销毁动作了吗?
    如果没有的话,对Test.a来说有何影响?
      

  5.   

    是的,楼主需要注意"析构函数"有“复活”一说。
    当某种类型实现了 Finalize() 后,该对象将进行特殊的看待:此类型对象放置在GC的特殊位置——终结列表。
    1、当进行第一次垃圾收集时,此类若没有其他引用,GC会对其进行回收,此时这些对象被认为是垃圾。进行GC.Collect 时,这些垃圾会被运送到另一个位置—— 终结可达队列。而终结可达队列,类似于全局或静态变量,会被当成一种GCRoot;
    2、被放置在此队列(终结可达队列)中的对象,认为是可以访问的,因此可以认为这些对象原先被当成垃圾的对象【1中提到的对象】得到了“复活”,
    3、当第一次 GC.Collect 执行后,GC会清空终结可达队列,此时【1】中的对象才是真正的垃圾。
    4、等待下次执行 GC.Collect 时,【1】中对象才能完全被清理掉其次,实现了 Finalize() 的对象,微软不保证其执行顺序,即:A,B 的Finalze的执行顺序是不保证的,因此不建议在A的 Finalize中访问B,或反推。因此,在实现Finalize时,微软推荐做法是同时实现 IDisposable 和 Finalize() ,即: IDisposable模式【直接取MSDN的实例】using System;
    using System.ComponentModel;// The following example demonstrates how to create
    // a resource class that implements the IDisposable interface
    // and the IDisposable.Dispose method.public class DisposeExample
    {
        // A base class that implements IDisposable.
        // By implementing IDisposable, you are announcing that
        // instances of this type allocate scarce resources.
        public class MyResource: IDisposable
        {
            // Pointer to an external unmanaged resource.
            private IntPtr handle;
            // Other managed resource this class uses.
            private Component component = new Component();
            // Track whether Dispose has been called.
            private bool disposed = false;        // The class constructor.
            public MyResource(IntPtr handle)
            {
                this.handle = handle;
            }        // Implement IDisposable.
            // Do not make this method virtual.
            // A derived class should not be able to override this method.
            public void Dispose()
            {
                Dispose(true);  // 【 这里的参数是 true】
                // This object will be cleaned up by the Dispose method.
                // Therefore, you should call GC.SupressFinalize to
                // take this object off the finalization queue
                // and prevent finalization code for this object
                // from executing a second time.
                GC.SuppressFinalize(this);
            }        // Dispose(bool disposing) executes in two distinct scenarios.
            // If disposing equals true, the method has been called directly
            // or indirectly by a user's code. Managed and unmanaged resources
            // can be disposed.
            // If disposing equals false, the method has been called by the
            // runtime from inside the finalizer and you should not reference
            // other objects. Only unmanaged resources can be disposed.
            private void Dispose(bool disposing)
            {
                // Check to see if Dispose has already been called.
                if(!this.disposed)
                {
                    // If disposing equals true, dispose all managed
                    // and unmanaged resources.
                    if(disposing)
                    {
                        // Dispose managed resources. 【Dispose的参数 true】
                        // 此时 托管对象是可以确保稳定的
                        component.Dispose();
                    }                // Call the appropriate methods to clean up
                    // unmanaged resources here.
                    // If disposing is false,
                    // only the following code is executed.
                    // 【Finalize 的参数 false => 只能保证非托管的是稳定的】
                    CloseHandle(handle);
                    handle = IntPtr.Zero;                // Note disposing has been done.
                    disposed = true;            }
            }        // Use interop to call the method necessary
            // to clean up the unmanaged resource.
            [System.Runtime.InteropServices.DllImport("Kernel32")]
            private extern static Boolean CloseHandle(IntPtr handle);        // Use C# destructor syntax for finalization code.
            // This destructor will run only if the Dispose method
            // does not get called.
            // It gives your base class the opportunity to finalize.
            // Do not provide destructors in types derived from this class.
            ~MyResource()
            {
                // Do not re-create Dispose clean-up code here.
                // Calling Dispose(false) is optimal in terms of
                // readability and maintainability.
                Dispose(false);  // 【 这里的参数是 false 】
            }
        }
        public static void Main()
        {
            // Insert code here to create
            // and use the MyResource object.
        }
    }
      

  6.   

    @AloneSword(孤剑):
    首先感谢您真诚的讲解,经过认真阅读您的解释后我还有一个疑问请您看到后能够给于回复,在此再次谢了!!
        问题1:这里被视为垃圾的含义是不是只是当成垃圾看待,而并非为真正的垃圾?而只有当当执行GC.Collect方法时垃圾回收器将其指向该对象的指针添加到终结可达队列当中,并在执行完GC.Collect方法后清空其列表后,在下一轮垃圾回收时A对象才被认为是真正的垃圾。并且A在终结可达队列中时A对象是认为可以被访问的,所以才有了在B.Finalize方法中的将A实例引用指向了Test.a的一幕。
        问题2:另外看到您取了MSDN中的例子里,执行MyResourse方法时,释放非托管资源时,是不是将对象内部的实例域以及静态域至为0或null,便是执行了析构动作呢?这里的操作和实际应用操作当中是相同的吗?
      

  7.   

    问题1: 对 
    问题2:非托管资源的释放与托管资源释放不一样,即使在将非托管对象置为null 或 null,其资源也不一定能释放。这个主要是非托管的资源的资源需手动释放【主要是历史问题,dotnet 之前没有GC的原因】。
    其次,将托管对象(不包括静态变量,静态变量被GC当成root,这个可以用 windbg 之类的调试器看到)置为null 或 0,并不能“马上”释放其资源,什么时间执行析构,是GC决定的,主要是如下几个条件(可能不完全,可以查找相关内容):
    .0代对象充满
    .显示调用GC.Collect();
    .内存不足
    .CLR 程序域被卸载
    .CLR 停止
    ps:楼主的学习精神不错,在这里能看到问这些问题的的确很少。若想更了解CLR和GC的东西,可以看看《架构设计-CLR via c#》 ,加上阅读 SSCLI2 代码。
      

  8.   

    隐含操作的,程序员 不知何时析构函数会被调用,这些由.net的GC自己去做的