垃圾收集问题——是不是bug

mybase89 2008-08-31 09:20:38
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?
...全文
127 12 打赏 收藏 转发到动态 举报
写回复
用AI写文章
12 条回复
切换为时间正序
请发表友善的回复…
发表回复
fuadam 2008-09-03
  • 打赏
  • 举报
回复
[Quote=引用 11 楼 gomoku 的回复:]
引用 10 楼 fuadam 的回复:
真理真是难以发现的,gomoku说的对吗?


请指出不对的地方。
[/Quote]
你说对了一半,不对的地方是不是因为调试保存了局部变量的值而是debug下没有优化,release下进行了优化而已,如果在release下你在
GC.Collect();
GC.WaitForPendingFinalizers();
后面加入
string s = to.ToString();

他的运行结果就会和debug下的效果相同
mybase89 2008-08-31
  • 打赏
  • 举报
回复
谢谢各位。3楼是对的
zzultc 2008-08-31
  • 打赏
  • 举报
回复
刚才试了试,在debug下确实第一次没有引发析构。学习中。
zzultc 2008-08-31
  • 打赏
  • 举报
回复
呵呵,顺便说一下,我是在析构函数里加里一个MessageBox来确定的。在Release下也是如此。
zzultc 2008-08-31
  • 打赏
  • 举报
回复
不知道你用的是.net哪个版本的。我的.net2.0没有你说的第一种情况。
我的解释如下:
对象析构并不是垃圾回收,只不过会在垃圾回收之前调用,垃圾回收是由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真正的回收。
fuadam 2008-08-31
  • 打赏
  • 举报
回复
[Quote=引用 1 楼 supper168 的回复:]
好好看看析构函数的使用吧!
[/Quote]
请你先了解下gc回收的机制吧!
fuadam 2008-08-31
  • 打赏
  • 举报
回复
[Quote=引用 3 楼 lextm 的回复:]
Effective C#里面有更加详细的说明。基本上不是.NET本身的问题。
[/Quote]
不是.net本书的问题?也就是说不是垃圾回收的问题,你确定?
lextm 2008-08-31
  • 打赏
  • 举报
回复
Effective C#里面有更加详细的说明。基本上不是.NET本身的问题。
gomoku 2008-08-31
  • 打赏
  • 举报
回复
1,在调试时,Visual Studio提供了显示即时值的功能,比如让你的breakpoint 2的地方,能够检查tc的值。
要检查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)。
春天的气息 2008-08-31
  • 打赏
  • 举报
回复
好好看看析构函数的使用吧!
gomoku 2008-08-31
  • 打赏
  • 举报
回复
[Quote=引用 10 楼 fuadam 的回复:]
真理真是难以发现的,gomoku说的对吗?
[/Quote]

请指出不对的地方。
fuadam 2008-08-31
  • 打赏
  • 举报
回复
真理真是难以发现的,gomoku说的对吗?

110,534

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术 C#
社区管理员
  • C#
  • Web++
  • by_封爱
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

让您成为最强悍的C#开发者

试试用AI创作助手写篇文章吧