当会在堆栈上的JVM商店成员变量的引用?

问题描述 投票:1回答:2

我读section 12.6.1 of Java SE specs,它说:

一个程序的优化转换可以设计降低可达小于那些会天真地被认为是可到达的对象的数量。例如,一个Java编译器或代码生成器可以选择设置将不再被使用为null以使存储对这样的对象是潜在可回收越快的变量或参数。

如果在对象的字段中的值被存储在寄存器发生的另一个例子。然后该程序可以访问寄存器,而不是对象,永远不会再访问该对象。这将意味着,对象是垃圾。需要注意的是这种优化的,如果引用是在栈上只允许,不存储在堆中。

相关的代码是:

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

我的问题是什么样的JVM会永远保存finalizerGuardian在堆栈不是堆的,为什么?

java garbage-collection jvm
2个回答
2
投票

该代码示例是说明你引用的文字的最后一句,“请注意,如果引用是在栈上这种优化仅允许,而不是存储在堆”,这是一个有点奇怪,你撕它关闭的解释文本:

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

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

如果子类覆盖super.finalize并没有明确调用finalize被称为终结守护力量super.finalize

如果这些优化允许为存储在堆中引用,那么Java编译器可以检测到finalizerGuardian场从来不看,空出来,立刻收集对象,早打电话来终结。这违背了原意:程序员可能想叫富终结时富实例变得无法访问。因此,这种转变是不合法的:内部类对象应该是可达的,只要外部类对象可达。

因此,代码示例说明的限制。通过本说明书提到的“优化转换”包括逃逸分析之后施加对象标量化已经证明,一个目的是纯粹本地的,换言之,在优化代码跨越对象的整个寿命。

不过,这并不需要这样的本地对象。前面已经提到的规范,优化代码可以保持一个对象的字段在CPU寄存器,而不需要重新阅读,因此,并不需要保持对象引用了。同样地,还在范围的参考变量可以是未使用的。如果该参照的是一个对象的唯一引用,从所述优化后的代码去除它允许较早垃圾收集。

这两种情况都将仍然允许Foo实例被淘汰或更早收集。这反过来会允许finalizerGuardian引用的对象(不再)的早期收集。但是,这并不抵消这种限制的意图。该规范限制优化,不允许内部对象获取早于外物收集,但在收集两者一起,其中包括早于预期天真没问题。

一般情况下,任意大的对象图可能会收集在一个垃圾收集周期,也许早于预期的天真,甚至得到完全优化掉。


1
投票

对于此类优化(逃逸分析)的典型例子是具有Point类的计算:


class Point {
    double x;
    double y;

    public Point(final double x, final double y) {
        this.x = x;
        this.y = y;
    }

    double length() {
        return Math.sqrt(x * x + y * y);
    }

    static double calc() {
        double result = 0;
        for (int i = 0; i < 100; i++) {
            // this allocation will be optimized 
            Point point = new Point(i, i);
            result += point.length();
        }
        return result;
    }
}

后内联这个new将不再需要,因为我们可以提取到局部变量像所有领域

Point point = new Point(i, i);
double x = point.x;
double y = point.y;
result += Math.sqrt(x * x + y * y);

->

Point point = new Point(i, i);
double x = i;
double y = i;
result += Math.sqrt(x * x + y * y);

现在很明显,new Point(i, i)是无用的,JIT只是删除此行。

需要注意的是分配是在一个局部变量堆栈即。如果它是在一个领域,因为它是存储在堆我们就无法做到这一点的优化。那它是如何工作的。

关于你的代码剪断:在finalizerGuardian总是会在现场(存储在堆)和JVM可以做什么与此分配。 Furemore如果从上面的例子中,类Point包含这样的领域,我觉得逃避analys无法删除分配,因为它可能会改变原来的行为。

© www.soinside.com 2019 - 2024. All rights reserved.