什麼是安全點?

在 JVM 中如何判斷對象可以被回收 一文中,我們知道 HotSpot 虛擬機採取的是可達性分析演算法。即通過 GC Roots 枚舉判定待回收的對象。

那麼,首先要找到哪些是 GC Roots。

有兩種查找 GC Roots 的方法:

一種是遍歷方法區和棧區查找(保守式 GC)。

一種是通過 OopMap 數據結構來記錄 GC Roots 的位置(準確式 GC)。

很明顯,保守式 GC 的成本太高。準確式 GC 的優點就是能夠讓虛擬機快速定位到 GC Roots。

對應 OopMap 的位置即可作為一個安全點(Safe Point)。

在執行 GC 操作時,所有的工作線程必須停頓,這就是所謂的"Stop-The-World"。

為什麼呢?

因為可達性分析演算法必須是在一個確保一致性的內存快照中進行。如果在分析的過程中對象引用關係還在不斷變化,分析結果的準確性就不能保證。

安全點意味著在這個點時,所有工作線程的狀態是確定的,JVM 就可以安全地執行 GC 。

如何選定安全點?

安全點太多,GC 過於頻繁,增大運行時負荷;安全點太少,GC 等待時間太長。

一般會在如下幾個位置選擇安全點:

1、循環的末尾

2、方法臨返回前

3、調用方法之後

4、拋異常的位置

為什麼選定這些位置作為安全點:

主要的目的就是避免程序長時間無法進入 Safe Point。比如 JVM 在做 GC 之前要等所有的應用線程進入安全點,如果有一個線程一直沒有進入安全點,就會導致 GC 時 JVM 停頓時間延長。比如這裡,超大的循環導致執行 GC 等待時間過長。

如何在 GC 發生時,所有線程都跑到最近的 Safe Point 上再停下來?

主要有兩種方式:

搶斷式中斷:在 GC 發生時,首先中斷所有線程,如果發現線程未執行到 Safe Point,就恢複線程讓其運行到 Safe Point 上。

主動式中斷:在 GC 發生時,不直接操作線程中斷,而是簡單地設置一個標誌,讓各個線程執行時主動輪詢這個標誌,發現中斷標誌為真時就自己中斷掛起。

JVM 採取的就是主動式中斷。輪詢標誌的地方和安全點是重合的。

安全區域又是什麼?

Safe Point 是對正在執行的線程設定的。

如果一個線程處於 Sleep 或中斷狀態,它就不能響應 JVM 的中斷請求,再運行到 Safe Point 上。

因此 JVM 引入了 Safe Region。

Safe Region 是指在一段代碼片段中,引用關係不會發生變化。在這個區域內的任意地方開始 GC 都是安全的。

線程在進入 Safe Region 的時候先標記自己已進入了 Safe Region,等到被喚醒時準備離開 Safe Region 時,先檢查能否離開,如果 GC 完成了,那麼線程可以離開,否則它必須等待直到收到安全離開的信號為止。

參考資料

深入理解Java虛擬機:JVM高級特性與最佳實踐(第2版)


推薦閱讀:
相关文章