1. 程式人生 > >深入理解JVM(③)低延遲的Shenandoah收集器

深入理解JVM(③)低延遲的Shenandoah收集器

### 前言 Shenandoah作為第一款不由Oracle(包括一起的Sun)公司的虛擬機器團隊所領導開發的HotSpot垃圾收集器。是隻存在於OpenJDK當中的,最初由RedHat公司建立的,在2014年的時候貢獻給了OpenJDK。 ### 與G1相比的優點 從程式碼的歷史淵源上來看,Shenandoah收集器更像是G1的下一代繼承者,兩者相似的堆記憶體佈局,在初始標記、併發標記等許多階段的處理思路都高度一致。 但是Shenandoah相比G1還是至少有三個明顯的不同之處。 1、==**支援併發的整理演算法,G1的回收階段是可以多執行緒並行的,但卻不鞥呢與使用者執行緒併發。**== 2、==**Shenandoah是預設不使用分代收集的,不會有專門的新生代Region或者老年代Region的存在。**== 3、==**Shenandoah摒棄了在G1中耗費大量記憶體和計算資源去維護的記憶集,改用名為“連線矩陣”(Connection Matrix)的全域性資料結果來記錄誇Region的引用關係降低了誇代維護的消耗。**== Shenandoah收集器的跨代“連線矩陣”示意圖 ![Shenandoah收集器連線矩陣](https://img-blog.csdnimg.cn/20200615234851583.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_SmlNb2Vy,size_60,color_c8cae6,t_70) 連線矩陣可以簡單的理解為一張二維表格,如果Region N有物件指向Region M,就在表格的N行M列中打上一個標記,如上圖所示,如果Region 5中的物件Object C引用了Region 3 的Object B,Object B又引用了Region 1 的Object A,那麼連線矩陣就中就會在5行3列、3行1列中打上標記。在回收時通過這張表格就可以得出哪些Region 之間產生了跨代引用。 ### 收集過程 Shenandoah收集器的工作過程大致可以劃分為以下九個階段: * **初始標記**:與G1一樣,首先標記與GC Roots直接關聯的物件,這個階段仍是“Stop The World”的,但停頓時間與堆大小無關,至於GC Roots的數量相關。 * **併發標記**:與G1一樣,編輯物件圖,標記出全部可達的物件,與使用者執行緒一起併發,時間長短與堆中存活物件的數量以及物件圖的結構複雜程度有關。 * **最終標記**:與G1一樣,處理剩餘的SATB掃描,並在這個階段統計出回收價值最高的Region,將這些Region構成一組==回收集==。此階段也會有一小段短暫的停頓。 * **併發清理**:這個階段用於清理那些整個區域內連一個存活物件都沒有找到的Region。 * **併發回收**:這個階段是Shenandoah與之前HotSpot中其他收集器的核心差異。在這個階段,Shenandoah要把回收集裡面的存活物件先複製一份到其他未被使用的Region中。但是有個難點是在移動物件的同時,使用者執行緒仍然可能不停的對被移動的物件進行讀寫訪問,移動物件之後整個記憶體中所有指向該物件的引用都還是舊物件的地址,這是很難一瞬間全部改變過來的。==**對於這個難點,Shenandoah將會通過讀屏障和被稱為“Brooks Pointers”的轉發指標來解決**==。 併發回收階段執行時間的長短取決於==回收集==的大小。 * **初始引用更新**:併發回收階段複製物件結束後,還需要把堆中所有指向舊物件的引用修正蛋糕複製後的新地址,這個操作稱為引用更新。這個階段就是對這個操作進行初始化的,初始引用更新時間很短,會產生一個非常短暫的停頓。 * **併發引用更新**:真正開始進行引用更新操作,這個階段是與使用者執行緒一起併發的,時間長短取決於記憶體中涉及的引用數量的多少。 * **最終引用更新**:解決了堆中的引用更新後,還要修正存在於GC Roots 中的引用。這個階段是Shenandoah的最後一次停頓,時間長短與GC Roots的數量有關。 * **併發清理**:經過併發回收和引用更新之後,整個回收集中所有的Region已再無存活物件,最後再呼叫一次併發清理過程來回收這些Region 的記憶體空間,供以後新物件分配使用。 **** 這九個階段的工作過程可能拆的比較瑣碎,只要抓住其中三個最重要的併發節點(併發標記、併發回收、併發引用更新)就好理解Shenandoah的運作過程了。 *** ### 轉發指標(Brooks Pointer) Shenandoah收集器的併發回收的核心是,轉發指標。 轉發指標的核心內容就是,在原有物件佈局結構的最前面統一增加一個新的引用欄位,在正常不處於併發移動的情況下,該引用指向物件自己。 如下圖: ![在這裡插入圖片描述](https://img-blog.csdnimg.cn/20200617214325972.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_SmlNb2Vy,size_16,color_c8cae6,t_70) 轉發指標加入後帶來的收益自然是當物件擁有了一份新的副本時,只需要修改一處指標的值,即舊物件上轉發指標的引用位置,使其指向新物件,便可將所有對該物件的訪問轉發到新的副本上。這樣只要物件的記憶體仍然存在,未被清理掉,虛擬機器記憶體中所有通過舊引用地址訪問的程式碼仍然可用,都會被自動轉發到新物件上繼續工作。 如下圖: ![Brooks Pointers](https://img-blog.csdnimg.cn/20200617215835403.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_SmlNb2Vy,size_60,color_c8cae6,t_70) Brooks Pointers 轉發指標在設計上決定了它是必然會出現多執行緒競爭問題的。Shenandoah收集器是通過比較交換(Compare And Swap,CAS)操作來保證併發時堆中的訪問正確性的。 ### 總結 1、Shenandoah收集器保證了收集垃圾的低延遲。 2、但是使用了過多的寫屏障,所以導致Shenandoah收集器的弱項很明顯,當資料量大的時候會產生高執行負擔而使得吞吐量