4

我刚刚读了这篇文章:关于垃圾收集的真相

“A.3.3 不可见”部分中,解释了对象如何以及何时进入invisible状态。

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

public void run() {
    try {
        Object foo = new Object();
        foo.doSomething();
    } catch (Exception e) {
        // whatever
    }
    while (true) { // do stuff } // loop forever
}

这篇文章中说:

但是,JVM 的有效实现不太可能在超出范围时将引用归零。

为什么没有效率?

我的解释尝试如下:

假设此方法的堆栈包含四个元素,现在不可见的对象位于底部。
如果要立即收集对象,则必须弹出并存储三个元素,弹出并丢弃第四个元素,然后将三个仍然有效的元素推回堆栈。
如果在控制流离开run方法后收集不可见对象,VM 可以简单地弹出所有四个元素并丢弃它们。

4

2 回答 2

3

局部变量不在操作数堆栈上,而是在激活框架中的局部变量区域中,在通过字节码和字节码引用的情况下访问aload局部astore变量,并且将局部变量置零不涉及任何推入和弹出。

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

  • 它不会导致立即的垃圾回收周期
  • 零可能很快会被程序逻辑所指示的另一个值覆盖。
  • 超出范围意味着局部变量不再是垃圾收集根集的一部分。因此,在超出范围之前它持有什么值(零或有效参考)并不重要;无论如何都不会被检查。

编辑:

对最后一句话的一些评论。

实际上,在字节码级别没有作用域,并且局部变量槽可能仍然是根集的一部分,直到方法返回。当然,JVM 实现可以确定局部变量槽何时失效(即方法返回的所有可能路径要么不访问变量要么是存储)并且不将其视为根集的一部分,但它是绝不需要这样做。

于 2011-11-19T16:48:19.123 回答
0

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

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

于 2011-11-19T16:38:27.703 回答