1. 程式人生 > >深入理解JVM(六) -- GC執行原則和方案

深入理解JVM(六) -- GC執行原則和方案

引用 The jvm 過程 類型 world 內存回收 解決 point

  上篇文章中,我們了解了Java虛擬機垃圾回收的思路和策略,這篇文章我們將了解Java是如何實現高效的回收算法的。

  我們需要了解,內存回收必須要保證“一致性”,意思就是在執行GC分析的時候,系統看起來要像是凍結在某一時間點上,不能出現在分析過程中,引用的情況還在發生變化,這樣就無法進行分析,這是為什麽在GC時必須要停頓所有的Java線程(官方稱之為Stop The World),Java是通過引用計數器和可達性分析算法來判斷一個對象是否可以被回收的,在進行可達性分析的時候,首先要枚舉出所有的根結點GC-Roots,一個一個去挑選顯然是不現實的,因為這會導致系統長時間的STW,而且,虛擬機是可以知道哪些地方存在存放著直接引用的,依靠使用一種叫做OOP的數據結構來達到這個目的,在類加載完成的時候,HotSpot就把對象內什麽偏移量上是什麽類型的數據計算出來,在JIT編譯的過程中,也會在特定的位置幾下棧和寄存器中哪些位置是引用,這樣在GC掃描時,就可以直接獲取這些信息了。

  上文也提到,並不會為每一條指令都創建OOP,因為這樣會產生大量的內存消耗,得不償失只會在特定位置創建,這個位置就是“安全點(Safe Point)”,程序只有在安全點才可以停下來執行GC,安全點設置的時候,為了追求合理(例如當需要執行GC時,不能讓GC等候太久才進入安全點),這個標準就是“是否會讓程序執行太久”,註意,這個判斷條件不能以指令集的長度來判斷,因為每條指令執行的時間都很短,其最明顯的特征就是指令序列復用,例如方法跳轉,循環跳轉,異常跳轉等,所以,具有這些功能的指令才會產生安全點。

  對於安全點,另一個需要考慮的問題是如何在GC發生時,讓所有的線程都跑到最近的安全點上停下來,這裏有兩個方案選擇:一是搶先式中斷,就是在GC發生時,將所有的線程中斷,如果發現有線程中斷的地方不在安全點上,就恢復該線程,現在幾乎沒有虛擬機采用這種搶先式中斷;而是主動式中斷,設置一個標誌,在GC發生時,該標誌為真,在每個安全點上去查看這個標誌,當這個標誌為真時,就自己掛起中斷。

  另外我們需要擴充一個安全域的概念,因為當有些線程正好處於Sleep或Block狀態時,無法響應JVM的暫停請求,JVM也不可能等待CPU重新分配時間片,這就需要安全域來解決,安全域是指在一個代碼片段中,引用關系不會發生變化,在這個區域中任何地方開始GC都是安全的,我們可以把安全域看作是對安全點的一個擴充。

  可以看出,JVM在具體的實現細節上遇到了很多問題,所以我們要深入了解JVM,學習優秀的解決方案,不能只停留在表面。

深入理解JVM(六) -- GC執行原則和方案