JVM學習之垃圾收集演算法
以下blog內容來自《深入理解Java虛擬機器_JVM高階特性與最佳實踐》,感謝作者!
昨天下班後地鐵上和以前的同學交談,才知道永遠不嘗試新東西永遠不會有新思路,希望寫完基礎系列之後自己能有所進步,然後分析更多有用的東西。
1垃圾收集演算法分類
垃圾收集演算法是如何收集物件,也就是如何回收堆及其他被jvm管理的可回收的記憶體的演算法,和書中一樣這裡只按照書本記錄垃圾收集演算法的思路。
垃圾收集演算法:
標記-清除演算法,複製演算法,標記整理演算法,分代收集演算法。
2 標記清除演算法
是最基礎的垃圾收集演算法,之後的演算法都是在此基礎上的改進,標記-清除演算法分成兩個階段,標記和清除;
過程:標記出所有需要回收的物件,標記完成後統一回收所有被標記的物件(怎麼判斷應該被回收請看)。
存在的問題:
(1)效率不高,標記和清除兩個過程效率都不高,
(2)空間問題,標記清除後會產生大量不連續的記憶體碎片(當需要分配大物件時,不連續的記憶體空間無法使用造成浪費)。
借用書中圖:
3 複製演算法
複製演算法為解決標記清除演算法的效率問題,該演算法將可用記憶體劃分成大小相同的兩塊,每次只使用其中一塊。
具體過程首先用一塊記憶體儲存物件,當這塊記憶體用完時就將還存活著的物件複製到另外一塊上面,然後把已使用過的記憶體空間 一次清理掉。所以每次都是先把存活的物件複製到另外一塊記憶體,然後直接清理掉當前的使用的塊的全部記憶體,效率會更高,
缺點:
可以看到每次只能使用總記憶體的一半,所以這種演算法比標記清除更浪費記憶體。
雖然浪費記憶體但當今使用的主流JVM的垃圾收集演算法都是採用這種收集演算法回收新生代,但並不是按照1:1的比例來劃分記憶體(絕大多數物件朝生夕死),將記憶體劃分成一塊較大的Eden和兩個較小的Survivor空間,每次使用Eden和其中的一塊Survivor,回收時將Eden和Survivor中還存活的物件一次性複製到另外一個Survivor空間上,之後清理掉Eden和上次使用的Survivor(預設每次存活物件較少,否則可能造成記憶體不夠用,之後需要額外的空間進行分配擔保)。
4 標記-整理演算法
複製演算法在存活物件較多時效率會降低,浪費的記憶體也會更多,所以老年代不適合這種演算法(老年代存活物件較多)。
標記-整理演算法符合老年代的特點,也是首先進行標記,之後不是直接清除而是讓所有存活物件向一端移動,這樣就不會存在記憶體碎片,最後清理掉端邊界以外的記憶體。
5分代收集演算法
分代收集演算法把記憶體劃分成新生代和老年代,新生代物件存活較少,老年代存活物件較多,所以兩個區域採用不同的垃圾收集演算法,新生代利用複製演算法,老年代利用標記-整理或者標記-清除演算法進行物件回收。