Hotspot是如何發起內存回收的?安全
1:枚舉根節點數據結構
以從 GC Root 節點找引用鏈操做爲例。spa
若在全局性的引用(常量、靜態類屬性)與執行上下文(棧幀中本地變量表)中查找引用鏈,因爲如今不少應用僅僅方法區就有數百兆,若是逐個檢查,必然會消耗不少時間。線程
可達性分析對執行時間的敏感還體如今GC停頓上,由於分析工做必須在一個可以確保一致性的快照中進行。這裏的一致性是指在整個分析期間整個執行系統看起來就像被凍結在某個時間點上,不可出現分析過程當中對象引用關係不斷變化的狀況。這一點致使GC進行時必須停頓全部Java執行線程。(stop the word)對象
主流的Java虛擬機使用的都是準確式GC。因此當執行系統停頓下來後,並不須要一個不漏的檢查完全部執行上下文和全局引用的位置,虛擬機是有辦法直接得知哪些地方存在對象引用。在Hotspot中,是使用一組OopMap的數據結構來達到這個目的的。內存
在類加載完成後,HotSpot就把對象內什麼偏移量上是什麼數據計算出來,在編譯過程當中,也會在特定的位置記錄下棧和寄存器中哪些位置是引用。這樣GC在掃描的時候就能夠直接得知這些信息。虛擬機
2:安全點it
在OopMap的幫助下,HotSpot能夠快速的完成GC Roots 枚舉,可是若是每一條指令都生成對應的OopMap,將須要大量的額外空間,GC的空間成本會變得很高。io
實際上,HotSpot的確沒有每條指令都生成OopMap,只是在「特定的位置」記錄下來這些信息,這些位置稱爲安全點(Safepoint)。即程序執行時,並不是在全部的地方都能停頓下來開始GC,只有到達安全點才能暫停。編譯
Safepoint的選擇不能太少以致於讓GC等待太長時間,也不能太多以致於過於頻繁會過度增長運行時的負荷。因此安全點的選定是以程序「是否具備讓程序長時間執行的特徵」爲標準的。每條指令的執行時間都是很是短暫的,「長時間執行」的最明顯的特徵就是指令複用。(方法調用、循環跳轉、異常跳轉)
對Safepoint的另外一個問題就是如何在GC發生的時候讓全部線程都跑到最近的安全點上停頓下來。這就誕生了兩種中斷:搶先式中斷 和主動式中斷。
搶先式中斷:不須要線程執行代碼主動去配合,在發生GC的時候,首先把全部線程所有中斷,若是發現有線程中斷的地方不在安全點上,就恢復線程,讓它跑到安全點。(幾乎沒有虛擬機採用這個方式)
主動式中斷:當GC須要中斷線程的時候,不直接對線程操做,僅僅簡單的設置一個標誌,各個線程執行時主動去輪詢這個標誌,發現中斷標誌爲真就本身中斷掛起。輪詢標誌和安全點重合。
3:安全區域
Safepoint 機制保證了程序執行時,在短期內就能遇到可進入GC的Safepoint 。可是程序若是不執行的時候呢?(所謂不執行就是沒有分配CPU,例如線程處於Sleep或Blocked狀態,這時就沒法響應JVM中斷)
安全區域(Safe Region)是指在一段代碼片斷中,引用關係不會發生變化。這個區域中任何地方開始GC都是安全的。至關於一個大的安全點。
線程執行到Safe Region中的代碼時,首先標識本身進入了Safe Region,當在這段時間裏JVM要發起GC時,就不用管標識本身爲Safe Region 狀態線程了。在線程要離開Safe Region時候,他要檢查系統是否已經完成根節點枚舉(或者整個GC過程),若完成則線程繼續,不然就必須等待直到收到能夠安全離開標識爲止。
總結:
前提:在類加載完成後,HotSpot就把對象內什麼偏移量上是什麼數據計算出來,在編譯過程當中,也會在特定的位置記錄下棧和寄存器中哪些位置是引用。這樣GC在掃描的時候就能夠直接得知這些信息,在HotSpot中是使用OopMap結構存儲這下信息。
安全點:HotSpot中生成OopMap的位置。程序運行時只有達到安全點纔會中止。
主動式中斷:當GC須要中斷線程的時候,不直接對線程操做,僅僅簡單的設置一個標誌,各個線程執行時主動去輪詢這個標誌,發現中斷標誌爲真就本身中斷掛起。輪詢標誌和安全點重合。