2 回答

TA貢獻1876條經驗 獲得超7個贊
正如Java 是否在對象仍在范圍內時可以完成對象中所闡述的那樣?,局部變量不會阻止引用對象的垃圾收集?;蛘?,正如這個答案所說,范圍只是一個語言概念,與垃圾收集器無關。
我將再次引用規范的相關部分JLS §12.6.1:
可達對象是可以從任何活動線程的任何潛在持續計算中訪問的任何對象。
此外,我將答案的示例擴展到
class A {
static volatile boolean finalized;
Object b = new Object() {
@Override protected void finalize() {
System.out.println(this + " was finalized!");
finalized = true;
}
@Override public String toString() {
return "B@"+Integer.toHexString(hashCode());
}
};
@Override protected void finalize() {
System.out.println(this + " was finalized!");
}
@Override public String toString() {
return super.toString() + " with "+b;
}
public static void main(String[] args) {
A a = new A();
System.out.println("Created " + a);
for(int i = 0; !finalized; i++) {
if (i % 1_000_000 == 0)
System.gc();
}
System.out.println("finalized");
}
}
Created A@59a6e353 with B@6aaa5eb0
B@6aaa5eb0 was finalized!
finalized
A@59a6e353 with B@6aaa5eb0 was finalized!
這表明即使是變量在范圍內的方法也可以檢測到引用對象的終結。此外,從堆變量中引用也不一定會阻止垃圾收集,因為B對象不可訪問,因為當包含引用的對象也不可訪問時,沒有繼續計算可以訪問它。
值得強調的是,即使使用對象并不總是會阻止其垃圾回收。重要的是,正在進行的操作是否需要對象的內存,而不是每次訪問源代碼中的對象字段都必須在運行時導致實際的內存訪問。該規范指出:
可以設計優化程序的轉換,將可到達的對象的數量減少到比那些天真地認為是可到達的要少。[…]
如果對象字段中的值存儲在寄存器中,則會出現另一個示例。然后程序可能會訪問寄存器而不是對象,并且永遠不會再次訪問對象。這意味著該對象是垃圾。
這不僅僅是一個理論上的選擇。正如在 Java 8 中調用強可達對象的 finalize() 中所討論的,它甚至可能在對象上調用方法時發生,或者換句話說,this當實例方法仍在執行時,引用可能會被垃圾收集。
防止對象垃圾回收的唯一方法是,如果終結器也對對象進行同步,則對對象進行同步,或者調用Reference.reachabilityFence(object)Java 9 中添加的方法。柵欄方法的后期添加展示了優化器獲取的影響在早于想要的垃圾收集問題上,從版本到版本更好。當然,首選的解決方案是編寫完全不依賴于垃圾收集時間的代碼。

TA貢獻1712條經驗 獲得超3個贊
并不是所有的對象都在堆空間中。但這通常是正確的。Java 已擴展為具有堆棧本地對象,前提是 JVM 可以檢測到該對象將僅與堆棧幀一樣長。
現在對于堆上的對象,它們在方法中具有本地引用。在處理方法時,與方法運行關聯的堆棧幀包含局部變量引用。只要可以使用引用(包括仍在堆棧幀中),對象就不會被垃圾收集。
一旦引用被銷毀,并且正在運行的程序無法再訪問該對象(因為沒有可以訪問它的引用),那么垃圾收集器將收集它。
添加回答
舉報