Java JVM何时在堆栈上存储成员变量引用?

Java JVM何时在堆栈上存储成员变量引用?,java,garbage-collection,jvm,Java,Garbage Collection,Jvm,我在读,上面写着: 可以设计程序的优化转换,以减少可访问对象的数量,使其少于天真地认为可访问的对象的数量。例如,Java编译器或代码生成器可能会选择将不再使用的变量或参数设置为null,以使此类对象的存储可能更快地被回收 如果对象字段中的值存储在寄存器中,则会出现这种情况的另一个示例。然后程序可以访问寄存器而不是对象,并且不再访问对象。这意味着该对象是垃圾。请注意,只有当引用位于堆栈上而不是存储在堆中时,才允许进行这种优化 有关守则如下: class Foo { private fina

我在读,上面写着:

可以设计程序的优化转换,以减少可访问对象的数量,使其少于天真地认为可访问的对象的数量。例如,Java编译器或代码生成器可能会选择将不再使用的变量或参数设置为null,以使此类对象的存储可能更快地被回收

如果对象字段中的值存储在寄存器中,则会出现这种情况的另一个示例。然后程序可以访问寄存器而不是对象,并且不再访问对象。这意味着该对象是垃圾。请注意,只有当引用位于堆栈上而不是存储在堆中时,才允许进行这种优化

有关守则如下:

class Foo {
    private final Object finalizerGuardian = new Object() {
        protected void finalize() throws Throwable {
            /* finalize outer Foo object */
        }
    }
} 

我的问题是什么样的JVM会将finalizerGuardian存储在堆栈中而不是堆中?为什么?

这种优化(转义分析)的一个经典示例是使用
点计算
类:


类点{
双x;
双y;
公共点(最终双x,最终双y){
这个.x=x;
这个。y=y;
}
双倍长度(){
返回Math.sqrt(x*x+y*y);
}
静态双计算(){
双结果=0;
对于(int i=0;i<100;i++){
//这一分配将得到优化
点=新点(i,i);
结果+=点长度();
}
返回结果;
}
}
内联此
后,不需要新的
,因为我们可以将所有字段提取到局部变量,如

Point-Point=新点(i,i);
双x=点x;
双y=点y;
结果+=数学sqrt(x*x+y*y);
->

Point-Point=新点(i,i);
双x=i;
双y=i;
结果+=数学sqrt(x*x+y*y);
现在很明显,
新点(i,i)
是无用的,JIT只是删除了这一行

注意,分配在堆栈上,即在局部变量中。如果它在一个字段中,我们将无法进行优化,因为它存储在堆中。 这就是它的工作原理


关于你被剪掉的代码:
finalizerGuardian
将始终在字段中(存储在堆中),JVM对此分配无能为力。Furemore如果上述示例中的类
包含此类字段,我认为escape analys无法删除分配,因为它可能会改变原始行为。

代码示例用于说明引用文本的最后一句话,“请注意,只有当引用位于堆栈上而不是存储在堆中时,才允许进行此类优化”,并且您从解释文本中删除它有点奇怪:

例如,考虑终结符守护模式:

如果子类重写了
finalize
,并且没有显式调用
super.finalize
,那么finalizer guardian强制调用
super.finalize

如果允许对存储在堆上的引用进行这些优化,那么Java编译器可以检测到
finalizerGuardian
字段从未被读取,将其置空,立即收集对象,并尽早调用终结器。这与意图背道而驰:当Foo实例变得不可访问。因此,这种转换是不合法的:只要外部类对象是可访问的,内部类对象就应该是可访问的

因此,代码示例说明了一个限制。规范中提到的“优化转换”包括在转义分析证明对象是纯局部的之后应用的对象标量化,换句话说,优化中的代码跨越对象的整个生命周期

但是它不需要这样的本地对象。正如前面提到的规范,优化代码可以将对象的字段保留在CPU寄存器中,而无需重新读取它们,因此,不再需要保留对象引用。同样,仍然在范围内的引用变量也可以不使用。如果该引用是对ob的唯一引用但是,将其从优化代码中删除可以更早地进行垃圾收集

这两种情况都允许更早地删除或收集
Foo
实例。这反过来又允许更早地收集对象(不再)由
finalizerGuardian
引用。但这并不能抵消此限制的意图。规范限制优化以不允许内部对象比外部对象更早被收集,但将两者收集在一起没有问题,包括比原始预期的更早


通常,任意大的对象图可能会在一个垃圾收集周期内被收集,可能比天真的预期要早,甚至会被完全优化。

我想,你误读了它(我很难读懂)。优化JVM可能希望反汇编
Foo
及其所有部分,并将其全部存储在寄存器中,但“这种转换是不合法的:只要外部类对象是可访问的,内部类对象就应该是可访问的”@maaartinus实际上,规范并不禁止反汇编
Foo
实例(以及整个对象图)。它不允许的是在
Foo
仍在堆上时收集内部对象实例。或者更简单地说,它不允许在外部对象之前收集内部对象,而将两者收集在一起则没有问题(如果每个对象都必须单独收集,则会导致性能灾难).在这种情况下,重要的是要记住,
finalize
调用没有保证的顺序。我明白你的意思。所以,
 class Foo {
     private final Object finalizerGuardian = new Object() {
         protected void finalize() throws Throwable {
             /* finalize outer Foo object */
         }
     }
 }