java中的copy collector是如何从visting中跳过死对象的?

java中的copy collector是如何从visting中跳过死对象的?,java,garbage-collection,Java,Garbage Collection,我试图了解java中的副本收集器是如何工作的。但似乎有点令人困惑的是,拷贝收集器的优点是,它跳过了对所有应该被收集的对象的访问,并将活动对象移动到堆的另一半。因此,我的问题是gc如何在不访问死对象的情况下调用finalize,或者这不算作访问对象?我认为混淆之处在于“访问”的定义 最初,需要“访问”所有对象,通过强引用无法访问的对象放入各种内部队列(请参阅弱/软/幻影引用),包括终结器队列 在整个过程中,对象仍然被认为是可访问的,因此GC无法收集。(事实上,终结器的一个问题是可能会意外地恢复正在

我试图了解java中的副本收集器是如何工作的。但似乎有点令人困惑的是,拷贝收集器的优点是,它跳过了对所有应该被收集的对象的访问,并将活动对象移动到堆的另一半。因此,我的问题是gc如何在不访问死对象的情况下调用finalize,或者这不算作访问对象?

我认为混淆之处在于“访问”的定义

最初,需要“访问”所有对象,通过强引用无法访问的对象放入各种内部队列(请参阅弱/软/幻影引用),包括终结器队列

在整个过程中,对象仍然被认为是可访问的,因此GC无法收集。(事实上,终结器的一个问题是可能会意外地恢复正在死亡的对象。)一旦对象通过所有这些队列,它才被认为是完全可回收的

该语句的意思是,一旦上述所有内容都被排除,复制收集器就不需要重新访问它们,只需将活动对象(包括仍在终结器队列中的对象)复制到幸存者空间,并将其余对象留在后面

这与压缩收集器相比,压缩收集器必须检查整个堆,找到间隙的位置,并通过找出最适合间隙的活动对象来尝试最小化间隙(显然,实际的算法将比这更聪明:)

这里的折衷是,使用复制收集器,您只能使用堆的一半,并且必须复制所有活动对象,即使没有间隙。您得到的回报是一个完全压缩的堆,这有利于快速分配和缓存

所有这些使得拷贝收集器非常适合年轻一代的收集,而压缩收集器传统上用于终身世代



有趣的是,Java 7中引入的G1收集器提供了一种不同的方法:在这个系统中,堆被划分为许多大小相同的区域,并尝试确定哪些区域包含大部分垃圾,对它们进行优先级排序以便进行复制。

但是,is Not mark和compact做了同样的事情,它只移动活动的对象,finalize queue与Visition不同,我认为这里的finalize也不算Visite。@Shanky我添加了一些关于差异的详细信息需要记住的一点是,大多数对象都没有finalize方法,Java可以针对这种情况进行优化。我似乎记得,使用finalize方法的对象实际上与其他对象的处理方式不同(即,在较慢的路径上)。@JoachimSauer当创建具有
finalize()
方法的对象时,VM会在其旁边创建一个指向它的
FinalizerReference
对象。一旦对象无法通过其他方式访问,该引用就会被放入终结器队列中,终结器队列有一个线程作为其使用者,因此如果有很多对象在排队,那么处理它将需要相当长的时间。这甚至可能意味着GC被迫将对象移动到旧一代,这使得事情变得更糟。如果没有finalizer和任何其他非强引用,VM可以很高兴地立即将对象标记为不可访问。