据我所知,在像Java或C#这样的托管语言中,有一种称为垃圾收集器的东西,每隔一段时间检查一次是否有任何对象实例不再被引用,因此是完全孤立的,并且然后清除内存不足。但是如果程序中的任何变量都没有引用两个对象,而是相互引用(如事件订阅),则垃圾收集器将看到此引用而不清除内存中的对象。
这是为什么?为什么垃圾收集器不能确定任何对象都不能被正在运行的程序的任何活动部分引用并处理它们。
答案 0 :(得分:24)
您的推定是不正确的。如果GC“看到”(见下文)2个或更多对象的循环引用(在“标记”阶段期间)但未被任何其他对象或永久GC句柄(强引用)引用,则将收集这些对象(在此期间) '清扫'阶段)。
可以在this MSDN article和this blog post中找到对CLR垃圾收集器的深入了解。
注意:实际上,GC在标记阶段甚至没有“看到”这些类型的对象,因为它们无法访问,因此在扫描期间收集
答案 1 :(得分:7)
大多数GC不再使用引用计数。它们通常(在Java和.NET中都是这种情况)使用来自根对象的可达性。对象的根集是全局变量和堆栈引用的实例。直接或间接从该集合可以到达的任何东西都是活着的。内存的其余部分无法访问,因此容易被收集。
答案 2 :(得分:2)
我想补充一点,围绕事件订阅的问题通常围绕着订户&出版商有着截然不同的生命周期。
附上自己,例如到Windows窗体中的 App.Idle 事件,您的对象将在剩余的应用程序生存期内保持活动状态。为什么?该静态应用程序将为您注册的观察者提供参考(尽管通过代理间接)。即使您可能已经处置了您的观察者,它仍然附加到App.Idle。您可以构建其中许多示例。
答案 3 :(得分:2)
这是传统reference counting garbage collection的主要缺点。描述此行为的垃圾收集器的属性是不完整的收集器。其他收藏家大致属于一个名为追踪垃圾收集器的类别,其中包括传统的标记扫描,半空间/压缩和世代混合,并且没有这些缺点(但面对其他几个)
我知道使用完整收集器的所有JVM和CLI实现,这意味着它们不会遇到您在此处询问的特定问题。据我所知,那些Jikes RVM中唯一一个提供引用计数收集器(其中一个)。
另一个有趣的事情是引用计数垃圾收集中存在完整性问题的解决方案,并且resulting collectors演示了一些难以从跟踪收集器中获取的有趣性能属性。不幸的是,性能最高的引用计数垃圾收集算法和大多数完整性修改依赖于编译器的帮助,因此将它们带到C ++的shared_ptr<T>
很困难/不会发生。相反,我们有weak_ptr<T>
和documented rules(对于次优链接 - 显然是文档中没有我抱歉)关于简单地避免问题。这个isn't the first time(另一个平庸的链接)我们已经看到了这种方法,希望防止内存问题的额外工作少于维护不使用shared_ptr<T>
的代码所需的工作量等等。
平庸的链接是因为我的大部分参考资料都散布在上学期内存管理课的笔记中。
答案 4 :(得分:1)
这里的其他答案肯定是正确的; .NET根据对象的可达性进行垃圾收集。
我想补充一点:如果您想要更深入的信息,我可以推荐阅读Understanding Garbage Collection in .NET(Andrew Hunter的简单文章)。