我們知道大多數的垃圾收集器在在收集垃圾的時候會停頓所有的執行緒(Stop The World)來進行可達性分析,那麼如何快速找到GC Roots?執行緒應該在什麼地方停止呢?

一、快速找到GC ROOTS:OopMap

當所有執行緒停下來的時候,並不需要一個不漏的檢查完所有執行上下文和全域性引用位置,虛擬機器應該是有辦法直接知道哪些地方存放著物件引用。在HotSpot的實現中,是使用一組稱為OopMap的資料結構來達到目的的。

OopMap儲存兩種物件引用:

1、物件內的引用

在類載入完的時候,HotSpot就把物件內什麼偏移量上是什麼型別的資料計算出來。

2、棧、暫存器中引用

在JIT編輯過程中,也會在特定的位置記錄下棧和暫存器中哪些位置是引用。這樣,GC在掃描的時候就知道這些資訊了。

透過OopMap垃圾收集器就可以更快的找到GC Roots,並且更快的完成GC Roots的列舉,大概展示如下圖:

Java虛擬機器如何快速找到GC Roots?又是如何中斷執行緒?

二、執行緒中斷點:安全點(Safe Point)

1、定義

安全點就是程式能夠停頓的位置。即程式不是在任何時候停頓下來進行GC,只有到了安全點才去更新OopMap和停頓,等待GC完成在繼續執行。

2、分析

有了OopMap,HotSpot就能很快的完成GC Roots的枚舉了。但是問題來了,每一行程式碼都有可能使引用變化,就需要更新OopMap,在哪個位置去更新OopMap呢?如果每一行都執行一次更新,肯定是不科學的,所以就有了安全點(safe point)。

安全點設定太多肯定不行,造成執行壓力,太少的話兩個點之間太長,如果剛過第一個安全點然後要求GC,但是程式要執行到下一個安全點才能停下來,那麼GC等待的時間就太長了。

3、安全點選擇標準

是否就有讓程式長時間執行的特徵。

一條指令執行時間都很短,而一段程式一般不會說因為很長的指令流而造成長時間的執行,所以一般都是在指令複用的地方出現。比如:

方法呼叫、迴圈跳轉、異常跳轉

三、讓執行緒停下來的兩種方法

1、搶斷式中斷

在GC發生時,中斷直接所有執行緒,發現沒有在安全點的,再恢復執行緒讓他跑到安全點。現在幾乎沒有虛擬機器採用這種方式。

2、主動式中斷

當GC需要中斷執行緒時,設定一個標誌,各個執行緒去輪詢這個標誌,發現需要中斷,執行緒就自己中斷。輪詢點和安全點在一個地方,在加上建立物件需要分配記憶體的地方

實現方式:

設定一個記憶體不可讀,當執行緒訪問這個記憶體就會產生一個自陷異常訊號,預先註冊的異常處理器中捕獲這個異常暫停執行緒。透過一個指令和一個異常處理器就實現了這個功能。

四、休眠執行緒如何中斷:安全區域(Safe Region)

1、安全點不能解決的問題

安全點解決了正在執行的執行緒中斷問題,我們知道執行緒還有沒執行的狀態,比如執行緒是Sleep、Blocked狀態。這些執行緒不能自己走到安全點。如果休眠的執行緒在GC途中醒來,線上程執行到安全點之前就會有可能修改物件的引用關係。所以我們需要線上程醒來的時候如果正在GC那麼也中斷。

2、安全區域

安全區域就是在一段程式碼中引用關係不會發生變化。所以在這個區域內任何地方GC都是安全的。在執行到安全區的時執行緒會標識自己處於安全區中,當離開安全區時,就需要檢查系統是否已經完成列舉GC Roots(或者整個GC過程),如果已經完成那麼執行緒繼續執行,否則就等待。

3、安全區域舉例

執行緒的Sleep、Blocked(這個區域內當前執行緒肯定不會改變物件引用)就被包含在安全區中,也就是說只要執行緒Sleep那麼他就處於安全區,一旦Sleep時間到執行緒繼續執行,首先就要判斷是否能夠離開安全區。

五、GC基礎全面總結

最近三篇學習了:可達性分析、標記-清除演算法、標記-複製演算法、標記-整理演算法,再加上今天的OopMap、安全區域、安全點,就基本上把垃圾回收的基礎理論分析完成。

可以猜測GC大概流程:執行緒執行時設定OopMap,當需要GC時,所以執行中的執行緒跑到安全點然後中斷,不在執行中的執行緒處在安全安全區域。此時遍歷OopMap(相當於GC Roots)根據可達性分析,標記出存活物件(或者需回收物件),然後根據區域和具體實現進行清除演算法、複製演算法、整理演算法。GC完成執行緒繼續執行,如果在GC過程中從Sleep醒來的執行緒也可以繼續執行。

大意如下圖:

#FormatImgID_3##FormatImgID_4#

學習心得:

1、虛擬機器中實現所有執行緒到達安全點自動中斷功能只用了一個標誌位和一個異常處理器。為我們有時候遇到一些全域性功能時提供了一種解決思路。

2、既然正在休眠類的執行緒不能走到安全點,那就把執行緒休眠的周圍設定成一塊區域,離開區域的時候先問問能不能離開。

Java程式設計師日常學習筆記,如理解有誤歡迎各位交流討論!

Java虛擬機器如何快速找到GC Roots?又是如何中斷執行緒?