在可達性分析之前面臨的問題
可達性分析的時間敏感性,主要體現在搜索根節點,以及引用一致性上
- 搜索根節點:可作為GC Roots的節點主要在全局性的引用上(常量與靜態屬性)與執行上下文(棧中的本地變量表)中,但是對于大型應用,在方法區的空間大小就有幾百兆,每次搜索所有 GC Roots 時都將耗費大量的時間
- GC停頓:每次進行可達性分析時都需要保證在分析過程中引用關系的保持不變(引用一致性),這就導致進行GC時必須暫停所有java執行線程,即使在號稱不會發生GC停頓的CMS收集器中,枚舉根節點時用戶線程也是必須要停頓的。
為了改善上述的性能問題
OopMap(Ordinary object Pointer Map 普通對象指針 Map數據結構)
HotSpot JVM 在特定的位置存儲棧和寄存器中哪些位置是引用,這樣就不用每次GC時都去遍歷這些區域,節省了大量時間。
Safepoint(安全點)
- 當需要GC時,程序執行時并非在所有地方都能停頓下來進行GC,只有到達安全點時才能暫停。
- 理論上OopMap為了能在任意位置進行暫停GC,必須為每條命令生成對應的OopMap對象,這將需要大量的空間,GC空間成本將會增加,實際上HotSpot只會在安全點生成對應的OopMap來記錄這些引用。
GC發生時,應用程序如何暫停?
- 搶先式暫停:不需要線程的執行代碼進行配合,而是在GC時直接中斷暫停,然后判斷哪個線程沒有落在安全點上,如果沒有落在安全點則恢復線程使其運行到最近的安全點上。
- 主動式暫停:設置一個標志,每個線程執行到安全點或者創建對象需要分配內存空間的時候主動輪詢這個標志,發現為中斷標志時使自己中斷掛起。這是所有JVM的主要實現方式。
Safe Region(安全區域)
- 為了解決處于sleep和blocked狀態的線程沒有辦法響應JVM的中斷請求(或者主動輪詢中斷標志),始終沒有辦法走到安全點
- 安全區指的是一段代碼之中,引用關系不會發生變化,在這個區域中的任意地方開始對這個線程 GC 都是安全的。
過程
- 在線程執行到Safe Region的代碼時,首先標識自己已經進入了Safe Region,然后線程可以繼續運行,在這期間可以sleep或者blocked,但是離開安全區域必須檢查系統是否已經完成了根節點的枚舉或者是整個GC過程,如果為是,則離開,否則等待直到為是。
- 由于線程sleep或blocked時引用關系不再變化,而引用關系不再的變化的代碼位于安全區域,換句話說sleep或blocked總是在安全區域內,所以GC時不用使sleep或blocked的線程跑到安全點上,因為位于安全區域的線程不管狀態如何,都能保證在這段時間內引用是不在變化的。
總結
- 每個安全點、安全區域指的是某一個線程執行的位置和區間,當發生GC時,每個線程都到達各自對應的最近的安全點或安全區域,就可以保證引用一致性了。
- JVM負責設置中斷和恢復安全點的所有線程,GC 面向的是所有線程。
- 可達性分析后進行回收時線程是繼續執行還是暫停取決于垃圾收集器的執行方式(并行還是串行的)