finalization& Safe Point& Safe Region
- finalization機制概述
- 物件的3種狀態
- 銷毀物件之前具體判斷程序
- 演示例子
- 安全點與安全區域
- 安全點(Safepoint)
- 安全區域(Safe Region)
finalization機制概述
- 當垃圾回收時物件被銷毀前, 總會先呼叫物件的 finalize()方法(
被呼叫的前提是必須重寫此方法, 同時未被呼叫過, 因為此方法只會呼叫一次), 常用于處理資源釋放 如關閉檔案, 套接字, 資料庫連接等
物件的3種狀態
- 當一個物件與 GC Roots失去聯系時, 就意味著物件已經不再使用了. 通常進入垃圾回收時就會被回收, 但是如果重寫了 finalize()方法, 虛擬機不會立馬回收, 而呼叫 finalize()方法緩一次回收(也就是給了一次復活的機會)
- 可觸及的: 可達物件(與 GC Roots直接或間接的聯系著)
- 可復活的: 不可達物件(失去了與 GC Roots的聯系), 但有一次執行 finalize()方法緩一次回收的機會(此時可以撰寫復活相關邏輯)
- 不可觸及的: 已呼叫過一次 finalize()方法, 且沒有復活. 此時的狀態就是不可觸及的狀態. 此時進入回收, 將肯定會被回收
銷毀物件之前具體判斷程序
- 判定一個物件A是否可回收, 會經過兩次標記程序:
- 如果物件A與 GC Roots失去聯系, 則進行第一次標記
- 是否執行 finalize()方法的判斷程序:
(1) 物件A重寫了 finalize()方法(如沒有重寫意味著沒有復活的程序), 同時 finalize()方法已呼叫過一次, 則判定物件A為不可觸及的狀態
(2) 物件A重寫了 finalize()方法, 且還未執行過, 那么物件A會被插入到一種 F-Queue佇列(參考佇列)中, 由一個虛擬機自動創建的, 低優先級的 finalizer執行緒觸發其 finalize()方法執行
(3) - 稍后(finalizer執行緒的觸發后), 將會對 F-Queue佇列中的物件進行第二次標記. 此時如果物件A在 finalize()中復活了(也就是重新建立了, 與 GC Roots的聯系), 那么在此次標記時, 物件A會從’即將回收’的集合中移出. 之后, 如果物件A再次出現與 GC Roots失去聯系的情況, 會直接成為不可觸及的狀態, 因為 finalize()方法只會被呼叫一次
演示例子
public class FinalizeTestApp {
/** GC Roots*/
public static FinalizeTestApp objA;
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("invoking `finalize` by GC");
/** 賦予參考地址, 目的為復活*/
objA = this;
}
public static void main(String[] args) {
try {
objA = new FinalizeTestApp();
/** 洗掉參考地址*/
objA = null;
/** System.gc()或 Runtime.getRuntime().gc(); 會顯式的觸發 Full GC
* 即使直接呼叫 以上方法, 也無法保證對垃圾收集器的呼叫
* */
System.gc();
System.out.println("GC 1");
/** 由于 Finalizer執行緒優先級較低, 暫停2秒, 為了保證 GC的執行*/
Thread.sleep(2000);
if (objA == null) {
System.out.println("obj is dead");
} else {
System.out.println("obj is still alive");
}
/** 重復了上面代碼, 為了演示 finalize()的被呼叫次數*/
objA = null;
System.gc();
System.out.println("GC 2");
/** 由于 Finalizer執行緒優先級較低, 暫停2秒, 為了保證 GC的執行*/
Thread.sleep(2000);
if (objA == null) {
System.out.println("obj is dead");
} else {
System.out.println("obj is still alive");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
輸出:
> GC 1
> invoking `finalize` by GC
> obj is still alive
> GC 2
> obj is dead
- 注: 永遠不要主動呼叫物件的 finalize()方法, 應由垃圾回識訓制自動呼叫, 理由3點:
- 在 finalize()時可能會導致物件復活
- finalize()方法的執行時間是沒有保障的, 它完全由 GC執行緒決定, 極端情況下, 若不發生GC, 則 finalize()方法將沒有執行機會
- 一個糟糕的 finalize()會嚴重影響 GC的性能(重寫的情況)
- finalize()方法區別于 C++的解構式, 雖然比較相似, 但 Java是基于垃圾收集器的自動記憶體管理機制, 所以本質上不同于 C++的解構式
安全點與安全區域
安全點(Safepoint)
- 程式執行時并非在所有地方都能停頓下來開始 GC, 只有在特定的位置才能停頓下來進行 GC, 這些點成為安全點
- 當 JVM要觸發 GC, 偏向鎖解除等操作時, 所有的用戶執行緒都必須到達安全點
安全區域(Safe Region)
- 安全點保證了程式執行程序中的時間隔比較近的安全點. 但是有些線是處于睡眠/阻塞/等待執行的狀態(此時執行緒是無法回應 JVM的中斷請求的), 此時就會用到安全區域
- 安全區域是指在一段代碼片段中, 物件的參考關系不會發生變化, 在這個區域中的任何位置開始 GC都是安全的
- 當執行緒運行到安全區域的代碼片段時, 首先會標識已經進入了安全區域, 如果這段時間內發生 GC, JVM會忽略標識為安全區域的執行緒
- 當執行緒即將離開安全區域時, JVM會檢查是否已經完成 GC, 如果完成了, 則繼續運行, 否則執行緒必須等待直到收到可以安全離開安全區域的信號為止
如果您覺得有幫助,歡迎點贊哦 ~ 謝謝!!
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/136502.html
標籤:其他
