Java 不可见变量的垃圾收集

Java 不可见变量的垃圾收集,java,java-memory-model,Java,Java Memory Model,我有以下代码: void method() { Object o1 = new Object(); { Object o2 = new Object(); System.out.println(o2); } // any long operation } o2对象在执行长操作期间是否有资格进行垃圾收集?是,但这取决于JVM/JIT是否会对此进行优化以避免多余的堆栈操作 那就可以了 Object o1 = new Object(); Object

我有以下代码:

void method() {
   Object o1 = new Object();
   {
      Object o2 = new Object();
      System.out.println(o2);
   }
   // any long operation
}

o2对象在执行
长操作期间是否有资格进行垃圾收集?

是,但这取决于JVM/JIT是否会对此进行优化以避免多余的堆栈操作

那就可以了

Object o1 = new Object();
Object o2 = new Object();
System.out.println(o2);
// any long operation
许多编译器将对所有需要的局部变量进行分组,并计算出保留它们所需的最大空间(有些将被删除并保存在寄存器中),并相应地增加堆栈,只有在函数返回后才缩小堆栈


这意味着o2将根据GC保留在“可访问”内存中,除非它被另一个作用域中的另一个变量覆盖

您需要了解您的变量
o2和由指定的对象是不同的

变量o2实际上是一个指针(尽管Java更喜欢将其称为“引用”),并在自动堆栈框架中占用4或8个字节。此存储不是垃圾收集的,仅当您从过程返回时(或可能在退出
{}
括号时,取决于编译器实现)才会消失

只要
new object()
操作结束,o2中“指定的”(指向的)对象就基本上可用于可能的垃圾收集,o2中存在指向该对象的指针就是防止这种情况发生的唯一原因。一旦变量o2不再存在于堆栈帧中,或者在堆栈帧中存储了不同的指针值,则可以收集该对象


所以在你的特殊情况下,答案是“可能”。这取决于编译器和JIT如何处理
{}
,以及一些“运气”问题,即在退出
{}
块(而不是整个方法)后,o2的存储位置是否被重用用于其他用途。

JLS对可达性的定义是:

“可访问对象是可以在任何可能的环境中访问的任何对象 从任何活动线程进行计算。”

在这种情况下,在
println
调用返回之前,正在进行的组合理论上不再能够访问该引用。(我假设,
println(o2)
没有将其保存在某个地方。)


然而,在实践中,现有的JVM无法判断对象在调用期间变得不可访问,大多数JVM只有在。。。或者在<代码>氧气超出范围。即使这样,GC运行也不能保证删除该对象



注意:这与JLS并不矛盾,因为“可访问对象”测试实际上是告诉您对象何时不会被垃圾收集,而不是何时被垃圾收集。JLS小心地指定对象可能在无法访问后的某个时间点被最终确定并进行垃圾收集,但也可能永远不会最终确定并进行垃圾收集。

否。即使o2引用的对象无法访问,也不会进行垃圾收集。它处于可到达和不可到达之间的状态,称为“不可见”,因为引用变量o2仍然在堆栈上

要使对象可垃圾回收,请分配
o2=null
或将该块放入另一个函数中


来源:

你最好的选择是在你不再需要氧气的时候将其清空:o2=null;对于一件糟糕的东西来说,这并不值得。当然,如果实际上您有一个100000字节的某种类型的结构,那么它可能是值得的。@jbnize对于非全局结构来说是完全无用的。该对象超出范围,不再可访问,与引用是否为空无关。即使o2与活动的变量共享堆栈位置,我也不认为JVM有任何理由将该内存位置/寄存器(如果它知道它尚未被活动变量覆盖)视为GC的根?JVM可以确定在哪个安全点哪个内存位置包含有效对象以及何时不包含有效对象。。虽然不知道HS做得有多彻底?是的,但这需要研究指令指针和函数代码本身,将所有对象引用组合在一起更容易(根据需要重用它们)当GC出现时,它只需要扫描这些对象指针,而不考虑它是否仍然可以使用,但是JVM已经知道它在哪个安全点,以及当前对象指针在这个特定点的位置。因此,通过在每个安全点的根集中添加不必要的对象,您可以做更多的工作。