MSDN里有说:当窗体显示为模式对话框时,单击“关闭”按钮(窗体右上角带 X 的按钮)会隐藏窗体并将 DialogResult 属性设置为 DialogResult.Cancel。与无模式窗体不同,当用户单击对话框的关闭窗体按钮或设置 DialogResult 属性的值时,.NET Framework 不调用 Close 方法。窗体转而可以隐藏并可重新显示,而不用创建该对话框的新实例。因为未关闭显示为对话框的窗体,所以在您的应用程序不再需要该窗体时,必须调用该窗体的 Dispose 方法。当按下某个按钮后,要弹出一个form,用ShowDialog模式,这段代码写在一个button的click响应函数里,而最后没有Dispose,这样.net的垃圾收集机制能够自动进行GC吗?
我的理解是如果没有Dispose,form的窗口句柄还在,应该没法自动GC

解决方案 »

  1.   

    你的理解是对的。
    但是form在被垃圾收集时,Dispose会被调用。
    另外程序退出后,句柄也会都释放掉。这里有一个讨论,你可以看一下:
    Can Form.ShowDialog() cause memory leaks when not Disposed()?
    http://channel9.msdn.com/forums/TechOff/136011-Can-FormShowDialog-cause-memory-leaks-when-not-Disposed/
      

  2.   

    private void buttonX1_Click(object sender, EventArgs e)
    {
        Form2 f2 = new Form2();            
        f2.ShowDialog();             
    }
    如果是这样子,那buttonX1_Click函数执行完后,f2会被GC吗?
      

  3.   

    buttonX1_Click执行完之后,不会马上释放F2的内存。
    microsoft不承诺什么时候进行垃圾收集,垃圾收集遵循一定的策略(内存不够啊,空闲的时候啊什么的)。也不是一定要垃圾收集(例如进程生命周期很短,还没来得及进行垃圾收集),但是进程结束时会释放所有资源。
    因此,最好还是显示调用dispose函数。
      

  4.   


    很定会被GC掉,前提是CLR对象管理中,0代龄已经满了,GC会启动,它会检查对象是不是弱引用(你上面的函数执行完后,对象f2已经没有别的地方应用它了,那么它就会成为弱引用),如果是GC会先检查终结链表(终结链表:如果类型重写了Finalize方法,本类型创建的对象都会被指向终结链表),发现终结链表上有对象引用,CLR会将终结链表上的引用挂到终结队列上,这时候被挂到终结队列上的对象又成了强引用,这时候GC清理0代龄时发现要被清理的对象(f2,因为窗体类重写了Finalize方法)成了强引用就跳过被对象,继续清理下一个对象。等GC清理完成,会有一个特殊的线程来执行终结队列上对象的Finalize方法,等对象的Finalize方法被调用后,本对象就会退出终结队列,那么对象这时就成了弱引用,等到第2次GC启动时就会回收掉这些内存。所以重写了Finalize 方法的类型对象要比没有重写的类型对象在内存中存活的时间要长,要消耗内存更长时间。
    上面如果你主动调用dispose方法,就可以避免对象在GC第二次启动时候回收内存。因为dispose方面里面调用了GC.suppressFinalize(),这个方法可以取消对象在终结链表上的引用。所以GC启动时就可以直接回收对象内存了。上面说的对象会在第二次GC启动时回收掉,那是在理想的情况下,因为,对象管理中有0,1,2代龄,当0代龄中没有清理掉的对象会提升为1代龄,当1代龄没有清理的对象才会提升为2代龄。1代龄的清理需要等到1代龄内存满才会启动。微软默认0代龄内存大小为512K,1代龄为2M,2代龄为10M。将整个内存分开清理是为了提升GC的清理速度(内存越小清理越快)。
      

  5.   


    这位朋友你说的:“但是form在被垃圾收集时,Dispose会被调用。”
    其实GC不会调用Dispose方法,它调用的是Finalize方法,一般类型继承了IDispose接口都会重写Finalize方法,当然不重写Finalize方法也是可以的,但这样就不能做到双曾保险了,如果你只实现了IDispose接口,对象也显示调用了Dispose方法,那么不重写 Finalize方法也没有什么问题,但如果你忘记调用Dispose方法,那么GC就会直接回收对象内存,这时对象中调用的非托管代码就没有释放掉,内存泄露就出来了。如果你重写了Finalize方法,GC在你忘记显示调用Dispose方法时会调用Finalizie方法,那么非托管内存就会被回收掉。