我了解在Java中,如果一个对象没有任何引用与之关联,垃圾收集器会在以后的某个时刻将其回收。
但是垃圾收集器如何知道一个对象是否有引用与之关联?
垃圾收集器是否使用某种哈希表或表格?
编辑:
请注意,我不是在询问垃圾回收机制怎么工作。实际上,我并不是在问那个。
我的问题是具体地,垃圾收集器如何高效地知道哪些对象是活动的,哪些是无用的。
这就是为什么我在问题中说,垃圾回收是否维护一种哈希表或集合,并定期更新一个对象所引用的数量。
我了解在Java中,如果一个对象没有任何引用与之关联,垃圾收集器会在以后的某个时刻将其回收。
但是垃圾收集器如何知道一个对象是否有引用与之关联?
垃圾收集器是否使用某种哈希表或表格?
编辑:
请注意,我不是在询问垃圾回收机制怎么工作。实际上,我并不是在问那个。
我的问题是具体地,垃圾收集器如何高效地知道哪些对象是活动的,哪些是无用的。
这就是为什么我在问题中说,垃圾回收是否维护一种哈希表或集合,并定期更新一个对象所引用的数量。
Java有各种不同的垃圾收集策略,但它们基本上都通过跟踪哪些对象是从已知活动对象可达的来工作。
在文章《Java中垃圾回收的工作原理》中可以找到一个很好的总结,但如果您想了解更详细的信息,您应该查看《使用5.0 Java[tm]虚拟机调整垃圾收集》。
当一个对象在运行程序中的任何指针都无法访问时,它被认为是垃圾。最直接的垃圾回收算法只是遍历每个可达对象。剩下的对象被视为垃圾。这种方法所需的时间与活动对象的数量成正比,对于维护大量实时数据的大型应用程序来说是不可行的。顺便提一下,要小心尝试猜测垃圾回收策略,我知道有许多程序因为过度使用System.gc()
或不适当的-XX
选项而性能受到破坏。
GC会尽可能快地知道哪些对象可以被移除。您不需要管理此过程。
但您可以非常礼貌地使用System.gc()
请求GC运行。这只是向系统发出的一个提示。GC此时不必运行,也不必删除您特定的对象等。因为GC是“老大”,而我们(Java程序员)只是它的奴隶... :(
没有一种 高效的 方式 - 它仍然需要遍历堆,但是 有一种诡异的方式:当将堆分成较小的部分时(因此无需扫描整个堆),这就是我们拥有代际垃圾收集器的原因,使得扫描花费的时间更少。
当你的整个应用程序停止并且可以分析对象图形时,这相对“容易”回答。它始于 GC roots
(我会让您找到这些是什么的文档),但基本上这些是由GC
不收集的“根”。
白色
表示它与根没有连接,灰色
表示它的子图尚未遍历,黑色
表示已经遍历并与根相连。所以要知道现在究竟是什么死/活 - 你只需要把所有最初为白色的堆都染成黑色。一切 白色
都是垃圾。有趣的是,通过了解实际存活的内容, GC
确认了 "垃圾"。这里有一些图形可视化 here 举例。
但这是简单的情况:当您的应用程序完全停止(有时为几秒钟),您可以扫描堆。这称为STW
- 停止全球事件,通常人们不喜欢这种情况。这就是并行收集器所做的:停止一切,进行所有GC操作(包括查找垃圾),然后让应用程序线程开始运行。
当您的应用程序正在运行并且您正在扫描堆时会发生什么?Concurrently
? G1/CMS
就是这样做的。请考虑一下:当您的应用程序可以通过另一个线程更改该叶子时,您如何推断出图形中的叶子是否存在。
Shenandoah
例如通过“拦截”图形上的更改来解决此问题。在与您的应用程序并发运行时,它将捕获所有更改并将这些更改插入到一些线程本地特殊队列中,称为SATB Queues
(开始时快照队列);而不是直接更改堆。完成后,将发生非常短的STW
事件,并且将清空这些队列。在STW
下,计算由该排水“引起”的内容,即:图形的额外着色。这远远简化了,仅供参考。G1
和CMS
使用不同的方法,据我所知。
理论上,这个过程并不是很复杂,但同时实现它是最具挑战性的部分。