JVM - 解讀GC中的 Safe Point & Safe Region

在這裏插入圖片描述

Safe Point 安全點

在這裏插入圖片描述

思考: 如上圖 GC的時候,是不是可以馬上GC,而不用去care用戶線程 ?

答案肯定是不行的。 HotSpot中GC不是在任意位置都可以進入,而只能在safepoint處進入。

JVM在設計的時候在“特定位置”記錄了OopMap , 而這些位置被稱爲安全點。

簡單來說

安全點就是指代碼運行到這個地方,它的狀態是確定的, JVM就可以安全的進行一些操作,比如GC。

所以GC不是想什麼時候做就立即觸發的,是需要等待所有線程運行到安全點後才能觸發。

安全點主要解決的是如何停頓用戶線程。

這些特定的安全點位置主要有以下幾種:

  • 方法返回之前
  • 調用某個方法之後
  • 拋出異常的位置
  • 循環的末尾
  • …等等

安全點的選定的核心在於: 既不能太少 (太少的話用戶線程一直在跑,跑不到SafePoint, 那就沒法GC, 並且跑的過程中用戶線程也會創建對象,也要佔內存,本身需要GC,那就說明內存喫緊了) ,也不能太多 (太多太頻繁就意味着運行時內存負荷較高) 。

第二個問題需要考慮: 如何在GC時讓用戶線程都跑到最近的安全點,然後停下來。 JVM 採取的方式是主動式終端,不直接線程操作,僅簡單設置一個標誌位,各個程序執行的時候去輪詢這個標誌,一旦返現中斷標誌位真就自己在最近的安全點上主動掛起。

輪詢標誌的地方和安全點是重合的。

既然是輪詢,那必須得高效,HotSpot把輪詢的操作精簡到只有一條彙編指令的程度,使用的是內存保護陷阱的方式。


Safe Region 安全區域

安全似乎解決了如何停頓用戶線程,讓虛擬機進入GC狀態的問題了。 但如果程序“不執行”呢?

舉個例子,線程休眠 Thread.sleep(100_000) 休眠100秒,要等100秒才能運行到安全區域啊 ,這可咋玩? 或者用戶狀態Blocked了 ,這都不執行了,壓根就沒法跑到safe point點了。。。。。

JVM設計大神引入了 Safe Region 來解決類似問題。

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

當用戶線程執行到安全區裏的代碼是,會先標識自己進入了安全區域,那GC的時候就不管這些已經聲明自己在安全區域的線程了。


OopMap

GC 我們都知道是清理那些引用不可達的對象, 簡單來說 JVM怎樣才能夠判斷出所有位置上的數據是不是指向GC堆裏的引用 ?

從外部記錄下類型信息,存成映射表 , 這種數據結構被稱爲 OopMap 。

在HotSpot中,對象的類型信息裏有記錄自己的OopMap,記錄了在該類型的對象內什麼偏移量上是什麼類型的數據。

oopMap是一個附加的信息,告訴你棧上哪個位置本來是個什麼東西。

這個信息是在JIT編譯時跟機器碼一起產生的。因爲只有編譯器知道源代碼跟產生的代碼的對應關係。

每個方法可能會有好幾個oopMap,就是根據safepoint把一個方法的代碼分成幾段,每一段代碼一個oopMap,作用域自然也僅限於這一段代碼。

循環中引用多個對象,肯定會有多個變量,編譯後佔據棧上的多個位置。那這段代碼的oopMap就會包含多條記錄。

每個被JIT編譯過後的方法也會在一些特定的位置記錄下OopMap,記錄了執行到該方法的某條指令的時候,棧上和寄存器裏哪些位置是引用。

這樣GC在掃描棧的時候就會查詢這些OopMap就知道哪裏是引用了。這些特定的位置主要在:

  • 1、循環的末尾
  • 2、方法臨返回前 / 調用方法的call指令後
  • 3、可能拋異常的位置

這種位置被稱爲“安全點”(safepoint)。之所以要選擇一些特定的位置來記錄OopMap,是因爲如果對每條指令(的位置)都記錄OopMap的話,這些記錄就會比較大,那麼空間開銷會顯得不值得。

選用一些比較關鍵的點來記錄就能有效的縮小需要記錄的數據量,但仍然能達到區分引用的目的。

因此,HotSpot中GC不是在任意位置都可以進入,而只能在safepoint處進入。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章