如果下面的一些概念有些不清楚的可以先看深入理解JVM - 垃圾收集器。
Shenandoah是一款只有OpenJDK纔會包含的收集器,最開始由RedHat公司獨立發展後來貢獻給了OpenJDK,相比G1主要改進點在於:
- 支持併發的整理算法,Shenandoah的回收階段可以和用戶線程併發執行;
- Shenandoah 目前不使用分代收集,也就是沒有年輕代老年代的概念在裏面了;
- Shenandoah 摒棄了在G1中耗費大量內存和計算資源去維護的記憶集,改用名爲“連接矩陣”(Connection Matrix)的全局數據結構來記錄跨Region的引用關係,降低了處理跨代指針時的記憶集維護消耗,也降低了僞共享問題的發生概率。
Shenandoah收集器的工作過程
Shenandoah收集器的工作過程一共有九個階段,下圖只畫了最核心的三個階段併發標記、併發回收、併發引用更新。
- 初始標記(Initial Marking):與G1一樣,只標記與GC Roots直接關聯的對象,這個階段仍是“Stop The World”的,但停頓時間與堆大小無關,只與GC Roots的數量相關。
- 併發標記(Concurrent Marking) :與G1一樣,從GC Root開始對堆中對象進行可達性分析,找出存活的對象,可與用戶線程併發執行,不會造成停頓,時間的長度取決於堆中存活對象的數量和對象圖的結構複雜度。
- 最終標記(Final Marking):與G1一樣,處理剩餘的SATB掃描,並在這個階段統計出回收價值最高的Region,將這些Region構成一組回收集(Collection Set),會有一小段短暫的停頓。
- 併發清理(Concurrent Cleanup):這個階段用於清理那些整個區域內連一個存活對象都沒有找到的Region(這類Region被稱爲Immediate Garbage Region)。
- 併發回收(Concurrent Evacuation) :首先把回收集裏面的存活對象先複製一份到其他未被使用的Region之中,然後通過讀屏障和Brooks Pointers轉發指針技術來解決在垃圾回收期間用戶線程繼續讀寫被移動對象的問題,併發回收階段運行的時間長短取決於回收集的大小。
- 初始引用更新(Initial Update Reference):併發回收階段複製對象結束後,還需要把堆中所有指向舊對象的引用修正到複製後的新地址,這個操作稱爲引用更新。引用更新的初始化階段實際上並未做什麼具體的處理,設立這個階段只是爲了建立一個線程集合點,確保所有併發回收階段中進行的收集器線程都已完成分配給它們的對象移動任務而已。初始引用更新時間很短,會產生一個非常短暫的停頓。
- 併發引用更新(Concurrent Update Reference) :真正開始進行引用更新操作,這個階段是與用戶線程一起併發的,時間長短取決於內存中涉及的引用數量的多少。併發引用更新與併發標記不同,它不再需要沿着對象圖來搜索,只需要按照內存物理地址的順序,線性地搜索出引用類型,把舊值改爲新值即可。
- 最終引用更新(Final Update Reference):解決了堆中的引用更新後,還要修正存在於GCRoots中的引用。會產生一個非常短暫的停頓,停頓時間只與GC Roots的數量相關。
- 併發清理(Concurrent Cleanup):經過併發回收和引用更新之後,整個回收集中所有的Region已再無存活對象,所以最後再調用一次併發清理過程來回收這些Region的內存空間,供以後新對象分配使用。
連接矩陣
連接矩陣可以簡單理解爲一張二維表格,如果Region N有對象指向RegionM,就在表格的N行M列中打上一個標記,如圖所示,如果Region 5中的對象Baz引用了Region 3的Foo,Foo又引用了Region 1的Bar,那連接矩陣中的5行3列、3行1列就應該被打上標記。在回收時通過這張表格就可以得出哪些Region之間產生了跨代引用。
Brooks Pointer 轉發指針技術
複製對象這件事情如果將用戶線程凍結起來再做那是相當簡單的,但如果兩者必須要同時併發進行的話,就變得複雜起來了。其困難點是在移動對象的同時,用戶線程仍然可能不停對被移動的對象進行讀寫訪問,移動對象是一次性的行爲,但移動之後整個內存中所有指向該對象的引用都還是舊對象的地址,這是很難一瞬間全部改變過來的。Brooks Pointer 轉發指針技術是來實現對象移動與用戶程序併發的一種解決方案。
Brooks 在原有對象佈局結構的最前面統一增加一個新的引用字段,在正常不處於併發移動的情況下,該引用指向對象自己(類似句柄,一個是放在句柄池中,一個是放在對象頭前面),如圖:
在對象移動的時候我們只需要將Brooks Pointer 指向新對象,在對象訪問過程中,只通一條mov
指令就可以完成對新對象的訪問了,如圖:
當寫操作發生時,Shenandoah收集器是通過CAS(Compare And Swap)操作,來保證收集器線程或者用戶線程只有其中之一可以進行修改操作,以此來保證併發時對象訪問的正確性。
優缺點
- 優點:延遲低
- 缺點:高運行負擔使得吞吐量下降;使用大量的讀寫屏障,尤其是讀屏障,增大了系統的性能開銷;
參考
《深入理解JAVA虛擬機》