深入JVM垃圾回收演算法
1. 標記——清除演算法(Mark-Sweep)
標記——清除演算法是第一種使用和比較完善的垃圾回收演算法,演算法分為兩個過程:1、標記正所有需要回收的物件2、標記完成後清除被標記的物件。其標記的過程就是判斷物件有效性,執行可達性分析的過程。其執行流程圖大體如何下所示:

image.png
同樣,我們也藉助現實的場景進行描述。圖書管裡有好多人在看書,圖書管理員想要收集起沒有被看的書的時候,他決定讓所有正在看書的人站起來,然後詢問每個人:那一本書是不看的。詢問完所有的人之後,同學們做下繼續看書。這時候,圖書管理員開始尋找所有做過標記的書,把它們收集起來。
缺點:
1、每次進行垃圾回收時,會暫停當前使用者程式的執行(類似讓所有的同學站起來)
2、垃圾回收器需要間隔性的檢查,並且標記和清除的過程相對較慢。
3、在標記清除之後可能會產生大量記憶體碎片,導致一旦需要為大物件分配空間時,由於找不到足夠大的記憶體空間,而不得以引發另外一次GC過程。
2. 標記——複製演算法(Mark——Copy)
標記——複製儲存演算法通過採用雙區域交替使用這種方式解決了標記——清除演算法中效率低下的問題。它將可可用記憶體劃分為兩個等量的區域(使用區和空閒區),每次只使用一塊。當正在使用的區域需要進行垃圾回收時,存活的物件將被複制到另外一塊區域。原先被使用的區域被重置,轉為空閒區。其執行流程大體如下所示:

image.png
圖書管理員為了更好的發現不看的書,將圖書室一分為二(A區和B區),同一時刻只有一塊區域允許看書。開始時只允許在A區看書。當管理員想要回收A區不被看的書的時候,大喊一嗓子“正在看書的同學拿著你書到B區”。等所有人都到了B區後,圖書管理員只要把A區的書收集起來,就完成了任務。下一次收集的時候,則是要求同學帶著自己看的書從B區轉移到A區。如此迴圈往復即可。
缺點:
1、原有可用空間被縮小為1/2,空間利用率降低了。
2、過程中也會暫停當前應用的執行。
3. 標記——整理演算法(Mark——Compat)
標記-複製演算法在物件存活率較高的情況下就要進行較多的複製操作,更重要的是該演算法浪費一般的記憶體空間,為了解決該問題,出現了標記——整理演算法:其標記的過程和“標記-清除”演算法一樣,而整理的過程則是讓所有存活的物件都想另外一端移動,然後直接清理掉端邊界以外的記憶體。其執行流程大體如下所示:

image.png
缺點:
1、暫停當前應用的執行,非實時性的回收。
4. 分代收集演算法
分代收集演算法理論來源於統計學。IBM公司的專門研究發現,物件的生存週期總體可分為三種:新生代、老年代和永久代。因此可以根據各個年代的特點採用適當的垃圾回收演算法。比如新生代的物件在每次垃圾時都會有大量的物件死去,只有很少一部分存活,那就可以選擇標記-複製演算法。另外I,在新生代中每次死亡物件約佔98%,那麼在標記-複製演算法中就不需要按照1:1的比例來劃分記憶體區域,而是將新生代細分為了一塊較大的Eden和兩塊較小的Survivor區域,HotSpot中預設這兩塊區域的大小比例為8:2。每次新生代可用區域為Eden加上其中一塊Survivor區域,共90%的記憶體空間,這樣就只有10%的記憶體空間處在被閒置狀態。在進行垃圾回收時,存活的物件被轉移到原本處在“空閒的”Eden區域。如果某次垃圾回收後,存活物件所佔空間遠大於這10%的記憶體空間時,也就是Survivor空間不夠用時,需要額外的空間來擔保,通常是將這些物件轉移到老年代。對於老年代來說,大部分物件都處在存活狀態。同時,如果一個大物件要在該區域進行分配,而記憶體空間又不足,那麼在沒有外部記憶體空間擔保的情況下,就必須選用標記-清除或者標記-整理演算法來進行垃圾回收了。
總而言之,分代收集只是根據物件生存週期的不同來選擇不同的演算法,其本身並沒有任何新思想。
5. 增量收集演算法
以上所述的演算法,都存在一個缺點:在進行垃圾回首時需要暫停當前應用的執行,也就是這時候的垃圾回收執行緒不能和應用執行緒同時執行。如果我們想做到“在不打斷同學們看書的情況下,圖書管理員就可以收集沒有被看的書”,這也是增量收集演算法的目標,即在不中斷應用執行緒的狀態下垃圾回收執行緒也能進行垃圾回收。但是這裡需要面對的問題是:垃圾回收執行緒在標記階段標記好了,還沒來的及清除時,當前應用執行緒進行記憶體操作,以至於清除階段無法正確開展,類似的情況是:圖書管理員剛標記了《JAVA核心技術》這本書已經沒有人看了,等標記完後,卻發現這本書已經有人在看了。