JVM - 解读GC中的 Safe Point Safe Region
文章目錄
- Safe Point 安全點
- Safe Region 安全區域
- OopMap
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處進入。
總結
以上是生活随笔為你收集整理的JVM - 解读GC中的 Safe Point Safe Region的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JVM - ZGC初探
- 下一篇: JVM - 列出JVM默认参数及运行时生