static tc gto;public class tc
{
public int a=99;~tc()
{
a=-1; //set breakpoint 1
gto=this;
}
}private void button1_Click(object sender,EventArgs e)
{
tc to=new tc();
GC.Collect();
GC.WaitForPendingFinalizers();
return; // set breakpoint 2
}two problems:
click button once,it seem the ~tc() not execute at all.and it will excute at the second time clicking.why?
the second clicking,breakpoint 2, "gto" and "to", gto.a=-1,while to.a=99,why?
{
public int a=99;~tc()
{
a=-1; //set breakpoint 1
gto=this;
}
}private void button1_Click(object sender,EventArgs e)
{
tc to=new tc();
GC.Collect();
GC.WaitForPendingFinalizers();
return; // set breakpoint 2
}two problems:
click button once,it seem the ~tc() not execute at all.and it will excute at the second time clicking.why?
the second clicking,breakpoint 2, "gto" and "to", gto.a=-1,while to.a=99,why?
要检查tc的值,这就要求tc不能被垃圾回收,为此Visual Studio调试器插入了特殊代码以保证tc在离开button1_Click()以前不会被回收。2,Release版就不同了。你把例子用Release编译运行,就会发现,同Debug相反,~tc()在第一次GC.Collect时就被调用了。3,你的第二个疑问,则这样解释:第二次点击button1_Click的时候,调试器不再需要保留第一个tc实例,于是调用了tc的finalizer()。
设置a=-1,并用gto保存了第一个tc的实例。第二个tc实例则没有被尝试回收(见第一点解释),它的a当然还是99。4,在finalizer()中把自己赋值给gto,叫做复活。但由于已经调用过一次finalizer()了,以后回收被复活的实例时,
将不会再次调用它的finalizer(),除非在finalizer中显示地调用了GC.ReRegisterForFinalize(this)。
我的解释如下:
对象析构并不是垃圾回收,只不过会在垃圾回收之前调用,垃圾回收是由GC决定的。
你的程序:
第一次点击:to指向一个一个临时的tc对象,我们叫他tc1对象。然后,在断点2处返回时,临时变量to无效,tc1由于缺少被引用而被执行对象的析构,在析构中把tc1.a设置为1,但是由于第二步全局静态变量gto保留了一个对tc1的引用,这块内存实际上并没有释放,仍然由程序保留使用。你可以再做一个按钮,你会发现gto.a仍有可以使用。
我的是.net2.0,没有出现你说的不执行那种情况,只是有时候会滞后一点而已。第2次点击时:to指向一个新的tc2临时对象,而gto还是指向的老tc1对象,所以在断点2处会有不同。很明显,第2次点击后,tc2调用析构时,tc1由于缺乏引用会被GC真正的回收。
GC.Collect();
GC.WaitForPendingFinalizers();
后面加入
string s = to.ToString();他的运行结果就会和debug下的效果相同