24

我知道在 Java 中,如果一个对象不再有任何对它的引用,垃圾收集器会在一段时间后回收它。

但是垃圾收集器如何知道一个对象有没有关联的引用呢?

垃圾收集器是否使用某种哈希图或表?


编辑:

请注意,我不是在问 gc 通常是如何工作的。真的,我不是在问这个。

特别问gc 如何高效地知道哪些对象是活的,哪些是死的。

这就是为什么我在我的问题中说 gc 维护某种哈希图或集合,并不断更新对象的引用数?

4

5 回答 5

13

典型的现代 JVM 使用几种不同类型的垃圾收集器。

一种经常用于已存在一段时间的对象的类型称为Mark-and-Sweep。它基本上涉及从已知的“活动”对象(所谓的垃圾收集根)开始,跟踪所有对象引用链,并将每个可访问对象标记为“活动”。

完成此操作后,扫描阶段可以回收那些尚未标记为“活动”的对象。

为了使这个过程正常工作,JVM 必须知道每个对象引用在内存中的位置。这是垃圾收集器准确的必要条件(Java 就是这样)。

于 2012-05-14T17:06:03.963 回答
6

Java 有多种不同的垃圾收集策略,但它们基本上都是通过跟踪哪些对象可以从已知的活动对象中到达来工作的。

可以在文章How Garbage Collection works in Java中找到一个很好的总结,但对于真正的底层,你应该看看Tuning Garbage Collection with the 5.0 Java[tm] Virtual Machine

当一个对象不能再从正在运行的程序中的任何指针访问时,它就被认为是垃圾。最直接的垃圾收集算法只是迭代每个可到达的对象。剩下的任何对象都被视为垃圾。这种方法所花费的时间与活动对象的数量成正比,这对于维护大量活动数据的大型应用程序来说是令人望而却步的。

从 J2SE 平台 1.2 版开始,虚拟机结合了许多不同的垃圾收集算法,这些算法使用分代收集进行组合。虽然幼稚的垃圾收集检查堆中的每个活动对象,但分代收集利用大多数应用程序的几个经验观察属性来避免额外的工作。

这些观察到的特性中最重要的是婴儿死亡率。...

即,像迭代器这样的许多对象只存在很短的时间,因此较年轻的对象比更老的对象更有可能有资格进行垃圾收集。

有关更多最新的调整指南,请查看:

顺便说一句,小心尝试猜测您的垃圾收集策略,我知道许多程序的性能因过度使用System.gc()或不适当的-XX选项而被破坏。

于 2012-05-14T17:17:52.417 回答
2

GC 将知道可以尽快删除对象。您不需要管理此过程。

但是你可以非常礼貌地要求 GC 运行 using System.gc()。这只是系统的提示。GC 不需要在那个时候运行,它不需要删除你的特定对象等。因为 GC 是大老板,我们(Java 程序员)只是它的奴隶...... :(

于 2012-05-14T17:07:40.227 回答
0

没有有效的方法 - 它仍然需要遍历堆,但是有一种 hacky 方法:将堆分成更小的部分(因此不需要扫描整个堆)。这就是我们有分代垃圾收集器的原因,以便扫描花费更少的时间。

当您的整个应用程序停止并且您可以分析对象图时,这相对“容易”回答。这一切都从GC roots(我会让你找到这些是什么的文档)开始,但基本上这些都是“根”,不被GC.

从这里开始进行某种扫描,分析“活动”对象:与这些根有直接(或传递)连接的对象,因此不可回收。在图论中,这可以通过使用 3 种颜色来“着色/遍历”您的图形:黑色、灰色和白色。White表示它连接到根,grey表示它的子图尚未遍历,black表示已遍历并连接到根。所以基本上要知道现在到底什么是死/活 - 你只需要把你所有最初是白色的堆都涂成黑色。一切都是white垃圾。有趣的是,“垃圾”实际上是GC通过知道什么是活着的来识别的。例如。

但这是一个简单的场景:当您的应用程序完全停止(有时几秒钟)并且您可以扫描堆时。这被称为STW停止世界事件,人们通常讨厌这些。这就是并行收集器所做的:停止一切,做任何 GC 必须做的事情(包括寻找垃圾),然后让应用程序线程启动。

当您的应用程序正在运行并且您正在扫描堆时会发生什么?Concurrently? G1/CMS做这个。想一想:当您的应用程序可以通过不同的线程更改该叶子时,您如何推断图表中的叶子是否存在。

Shenandoah例如,通过“拦截”图形上的变化来解决这个问题。在与您的应用程序同时运行时,它将捕获所有更改并将这些更改插入一些线程本地特殊队列,称为SATB Queues(请求队列的快照);而不是直接改变堆。完成后,STW将发生一个非常短的事件,并且这些队列将被排空。仍然STW计算排水“引起”的情况下,即:图形的额外着色。 这很简单,仅供参考。G1CMS以不同的方式进行AFAIK。


所以理论上,这个过程并没有那么复杂,但同时实现它是最具挑战性的部分。

于 2019-11-30T20:41:05.180 回答
0

事实是,垃圾收集器通常不会快速知道哪些对象不再有任何传入引用。而且,事实上,即使有传入的引用,一个对象也可能是垃圾。

垃圾收集器使用对象图的遍历来查找可到达的对象。在此遍历中未到达的对象被视为垃圾,即使它们是引用循环的一部分。一个对象不可达与垃圾收集器实际收集该对象之间的延迟可能是任意长的。

于 2021-04-29T12:32:40.117 回答