Java 为什么';隐形';不立即收集的对象?

Java 为什么';隐形';不立即收集的对象?,java,garbage-collection,object-lifetime,Java,Garbage Collection,Object Lifetime,我刚刚读到这篇文章: 在“A.3.3不可见”一节中,解释了对象如何以及何时进入不可见状态 在下面的代码中,分配给变量foo的对象在离开try/catch块后将变为不可见,并将保持强引用,直到run方法退出(这将永远不会发生,因为while循环将永远运行) 本条规定: 然而,JVM的有效实现不可能为零 超出范围时的引用 为什么效率不高? 我试图解释如下: 假设此方法的堆栈包含四个元素,现在不可见的对象位于底部。 如果要立即收集对象,必须弹出并存储三个元素,弹出并丢弃第四个元素,然后将三个仍然有效的

我刚刚读到这篇文章:

在“A.3.3不可见”一节中,解释了对象如何以及何时进入
不可见
状态

在下面的代码中,分配给变量
foo
的对象在离开
try/catch
块后将变为
不可见
,并将保持强引用,直到
run
方法退出(这将永远不会发生,因为
while
循环将永远运行)

本条规定:

然而,JVM的有效实现不可能为零 超出范围时的引用

为什么效率不高?

我试图解释如下:

假设此方法的堆栈包含四个元素,现在不可见的对象位于底部。
如果要立即收集对象,必须弹出并存储三个元素,弹出并丢弃第四个元素,然后将三个仍然有效的元素推回到堆栈上。

如果在控制流离开
run
方法后收集不可见对象,VM可以简单地弹出所有四个元素并丢弃它们

非常简单的答案是b/c效率低下


有许多垃圾收集器算法,其中一些可能会主动收集。有些编译器在堆栈上进行分配,但在您的情况下最明显的是:
doSomething()
可能会在其他地方保留(泄漏)对对象的引用。

局部变量不在操作数堆栈上,而是在激活帧中的局部变量区域,可访问,在通过字节码和归零的
aload
astore
引用的情况下,局部变量不涉及任何推送和弹出

归零效率低下,因为不需要:

  • 它不会立即导致垃圾收集循环
  • 根据程序逻辑,零可能很快被另一个值覆盖
  • 超出范围意味着局部变量不再是垃圾收集根集的一部分。因此,在超出范围之前,其持有的价值(零或有效参考)无关紧要;反正也不会检查
编辑:

对最后一项声明的一些评论


实际上,在字节码级别上没有作用域,局部变量槽可能仍然是根集的一部分,直到方法返回为止。当然,JVM实现可以确定局部变量时隙何时死亡(即,所有可能的方法返回的路径要么不访问变量,要么存储),而不认为它是根集的一部分,但决不是需要这样做的。混淆代码也会混淆JVM。超出范围意味着局部变量不再是垃圾收集根集的一部分。这是不正确的,一个对象被认为是垃圾材料,当它实际上不再使用,即当没有更多的参考。例如
{Object a=new Object();a.hashCode();//如果不再引用它的垃圾材料}
@bestsss,也许您想说些别的,因为在这种形式下,您的注释没有多大意义?JVM不理解Java定义的范围。括号{}即使在字节码中也不定义作用域,确实存在“帧”,但它们不适用于作用域。在前面的注释中,即使在
a.hashCode()
之后存在无限循环,只要不再引用“a”,GC也可以自由收集“a”。至于编辑:JVM根本不需要收集任何东西,可以有一个不带任何垃圾收集器的JVM。
public void run() {
    try {
        Object foo = new Object();
        foo.doSomething();
    } catch (Exception e) {
        // whatever
    }
    while (true) { // do stuff } // loop forever
}