我试图在Windows窗体应用程序中运行内存泄漏.我现在正在查看包含多个嵌入表单的表单.让我担心的是,子构造在构造函数中引用父窗体,并将其保存在私有成员字段中.所以在我看来,垃圾收集时间到了:
Parent通过控件集合引用子表单(子表单嵌入在那里).子表单不是GC'd.
子表单通过私有成员字段引用父表单.父表单不是GC'd.
这是否准确了解垃圾收集器将如何评估这种情况?有什么办法可以"证明"它用于测试目的吗?
好问题!
不,两种形式都是(可以)GC,因为GC不直接在其他参考文献中查找引用.它只查找所谓的"根"引用...这包括堆栈上的引用变量,(变量在堆栈上,实际对象当然在堆上),引用CPU寄存器中的变量,以及引用变量类中的静态字段...
所有其他引用变量只有在上述过程找到的"根"引用对象之一的属性中被引用时才被访问(和GC'd)...(或者在根对象中引用引用的对象中)等...)
因此,只有当其中一个表单被引用到"根"引用中的其他位置时 - 这两个表单才能安全地从GC中获取.
我只能想到"证明"它,(不使用内存跟踪实用程序)将在方法中的循环中创建几十万个这样的表单,然后,在方法中,查看应用程序的内存占用,然后退出方法,调用GC,再次查看足迹.
正如其他人已经说过的那样,GC对循环引用没有任何问题.我想补充一点,在.NET中泄漏内存的常见位置是事件处理程序.如果您的某个表单有一个附加的事件处理程序到另一个"活着"的对象,那么就会有一个对表单的引用,表单将不会得到GC.
垃圾收集通过跟踪应用程序根来工作.应用程序根是包含对托管堆上对象的引用(或为null)的存储位置.在.NET中,根源是
对全局对象的引用
对静态对象的引用
对静态字段的引用
将堆栈引用到本地对象
将堆栈引用到传递给方法的对象参数
引用等待最终确定的对象
CPU中的引用注册到托管堆上的对象
活动根列表由CLR维护.垃圾收集器通过查看托管堆上的对象并查看应用程序仍可访问的对象(即可通过应用程序根目录访问)来工作.这样的对象被认为是根源.
现在假设您有一个父表单,其中包含对子表单的引用,这些子表单包含对父表单的引用.此外,假设应用程序不再包含对父表单或任何子表单的引用.然后,出于垃圾收集器的目的,这些托管对象不再是root用户,并且在下次发生垃圾收集时将被垃圾收集.
如果未引用父项和子项,但它们仅引用彼此,则它们会获得GCed.
获取内存分析器以真正检查您的应用程序并回答您的所有问题.我可以推荐http://memprofiler.com/