什么是解释垃圾收集如何工作的简单方法?

时间:2009-03-08 11:18:30

标签: language-agnostic garbage-collection

我的注意力很短,所以我无法完成Wikipedia article

我知道有几种垃圾收集技术,但常见的一种是“可达性”测试,其中一个对象的收集资格是基于它是否可以被一个有根对象“到达”(对我来说)理解是一个已知不需要收集的对象。当你想知道某个对象是否可以访问时,你会怎么做呢?你怎么知道在哪里看?

显然,收集器必须知道所有已分配的对象和有根对象。它如何确定每个对象的可达性?

7 个答案:

答案 0 :(得分:2)

通过走指针/参考,我会说。原则上,您只需查看对象是否仍具有指向它的引用(来自其他对象,当前执行代码的局部变量,......)。如果没有,那么就不能再次获得对这个对象的引用(至少在Java这样的语言中,至少在你不能做指针欺骗的地方),所以抛出那个特定的对象通常是安全的。

使用(或仍在使用)的其他方案是例如引用计数,其中每个对象都有一个对它的引用计数器,每当有人获得对该对象的引用时必须递增,并且每当有人失去对该对象的引用时递减宾语。如果我没记错的话,Windows中的COM会以这种方式工作。

Java和.NET使用(以及其他)分代垃圾收集,其中每个对象最初被假定为非常快速地死亡(世代假设)。然后它会进行一些优化以保持垃圾收集周期快速,从而不会破坏程序运行过多。在旧时代,GC在程序运行时锁定程序并不常见,有时持续几秒钟。

除此之外,GC通常只在内存不足时运行,i。即已经积累了太多的死物,需要回收。这就是为什么大多数托管应用程序与非托管应用程序相比似乎浪费了更多内存,即使在许多情况下,通过运行GC一次可以回收大部分内存。

答案 1 :(得分:1)

垃圾收集器将始终知道每个已分配的对象,否则它无法检测到无法访问的对象以进行删除。在内存分配期间,这一切都得到了解决。这是有道理的,因为如果垃圾收集器知道每个对象,那么就有可能形成无法到达的孤立对象集,甚至垃圾收集器都无法访问。基本上,这将是内存泄漏。因此,垃圾收集器至少必须知道每个已分配的对象。

这导致了垃圾收集器如何确定哪些对象符合删除条件的问题。有各种各样的技术,但你会听到很多关于“标记和扫描”和“引用计数”的两个(它们经常出现在教科书中),他们值得了解垃圾收集的基本思想。

标记和扫描涉及垃圾收集器遍历对象引用(从一组已知的不可收集对象开始)并标记它可以到达的每个对象(例如,在对象上设置标记)。当它耗尽所有引用时,可以在扫描阶段删除未标记的对象集。

引用计数涉及垃圾收集器为内存中的每个对象保留一个计数,其中有多少其他对象引用它。每次从另一个对象建立对象的引用时,此计数器将递增,并在删除引用时递减。当计数器命中0时,对象不再被任何东西引用,并且垃圾收集器知道它可以被安全删除(此时它基本上变得无法访问 - 没有对象“知道”此对象了。)

答案 2 :(得分:1)

当我们创建对象并将该对象赋值为null时,该对象已准备好被垃圾收集器删除...

答案 3 :(得分:1)

一种简单(但常见)的垃圾收集方法类似于在建筑物中找到有价值的东西并将其移动到新建筑物,然后炸毁旧建筑物并用空的建筑物替换它。为了最小化所需的物体移动量,系统可以使用一些不同尺寸的建筑物。所有新物体都进入一幢小楼。当它完整时,所有有用的东西都被复制到一个中型建筑物中,小型建筑物被炸毁并更换。如果在中型建筑物中留下的建筑物的价值不足,那么所有有用的东西都会被搬到一幢大楼里,然后中型建筑就会被炸毁并更换。如果大型建筑物似乎积累了太多垃圾,那么所有有用的东西都会被复制到另一座大型建筑物中,然后大楼将被炸毁并更换。虽然小型建筑物会被炸毁并取代很多,但其中的大部分内容都将是垃圾,不需要复制。同样,当媒体建筑需要更换时,其中的大部分内容都将被废弃,因此只需要将一小部分建筑物搬到大楼。复制大楼里的所有东西都会很昂贵,但这种操作不会经常发生。

答案 4 :(得分:0)

收集器必须知道每个线程在全局变量区域内以及每个函数/方法/过程调用的激活帧中对象引用的位置。处理器寄存器也可以包含对象引用。 某些信息必须由编译器或虚拟机提供。

答案 5 :(得分:0)

从根对象(即main()中的堆栈对象开始 - 等效)然后只需跟随每个指针/引用并将每个对象标记为“可达”。继续,直到您标记每个对象。剩下的对象(尚未标记)是垃圾。

另一种方法是引用计数,这也是垃圾收集,除了标记在每次指针写入时完成并且清理是静止的(一旦对象无法访问,它的引用计数为零,所以它是破坏)。请注意,重新计数需要标记和扫描阶段(如上所述)来解决周期。 (即彼此依赖的对象,但没有其他人关心的对象)

答案 6 :(得分:0)

您可以将正在运行的程序(可能我应该特定于Java)视为一些Threads。虚拟机可以将每个线程的根帧用作root。然后,它可以向下遍历从该根引用的所有内容的可达性树(以及根下的所有堆栈帧)。

无法访问任何其他内容