2 回答

TA貢獻2003條經(jīng)驗 獲得超2個贊
代碼示例用于說明引用文本的最后一句話,“請注意,僅當(dāng)引用在堆棧上而不是存儲在堆中時才允許進行這種優(yōu)化”,您將其從解釋中扯下來有點奇怪文本:
例如,考慮Finalizer Guardian模式:
class Foo {
private final Object finalizerGuardian = new Object() {
protected void finalize() throws Throwable {
/* finalize outer Foo object */
}
}
}
super.finalize如果子類覆蓋并且finalize未顯式調(diào)用super.finalize.
如果允許對存儲在堆上的引用進行這些優(yōu)化,那么 Java 編譯器可以檢測到該finalizerGuardian字段從未被讀取,將其清空,立即收集對象,并盡早調(diào)用終結(jié)器。這與意圖背道而馳:程序員可能想在 Foo 實例變得無法訪問時調(diào)用 Foo 終結(jié)器。因此,這種轉(zhuǎn)換是不合法的:只要外部類對象可訪問,內(nèi)部類對象就應(yīng)該是可訪問的。
所以代碼示例說明了一個限制。規(guī)范中提到的“優(yōu)化轉(zhuǎn)換”包括在逃逸分析證明對象是純本地的之后應(yīng)用的對象標(biāo)量化,換句話說,優(yōu)化下的代碼跨越對象的整個生命周期。
但它不需要這樣的本地對象。正如規(guī)范已經(jīng)提到的,優(yōu)化的代碼可以將對象的字段保留在 CPU 寄存器中,而無需重新讀取它們,因此,不再需要保留對象引用。同樣,仍然在范圍內(nèi)的引用變量可能未被使用。如果該引用是對某個對象的唯一引用,則將其從優(yōu)化代碼中刪除允許更早地進行垃圾回收。
這兩種情況仍然允許Foo實例被更早地消除或收集。這反過來將允許較早地收集 . 引用的對象(不再是)finalizerGuardian。但這并不能抵消這種限制的意圖。規(guī)范將優(yōu)化限制為不允許內(nèi)部對象早于外部對象被收集,但將兩者收集在一起沒有問題,包括早于天真的預(yù)期。
通常,任意大的對象圖可能會在單個垃圾收集周期中被收集,可能比天真預(yù)期的要早,甚至完全被優(yōu)化掉。

TA貢獻2039條經(jīng)驗 獲得超8個贊
這種優(yōu)化(逃逸分析)的一個經(jīng)典例子是一個帶有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;
}
}
內(nèi)聯(lián)后就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);
現(xiàn)在很明顯這new Point(i, i)是沒用的,JIT 只是刪除了這一行。
請注意,分配是在堆棧上,即在局部變量中。如果它在一個字段中,我們將無法進行優(yōu)化,因為它存儲在堆中。它是如何工作的。
關(guān)于您的代碼被剪斷:finalizerGuardian將始終在字段中(存儲在堆中),而 JVM 對此分配無能為力。此外,如果Point上面示例中的類包含此類字段,我認為轉(zhuǎn)義分析無法刪除分配,因為它可能會改變原始行為。
添加回答
舉報