天天看點

JVM系列二(垃圾收集算法).

一、标記-清除算法(Mark-Sweep)

這種算法分為“标記”和“清除”兩個階段:首先标記出所有需要回收的對象,在标記完成後統一回收所有被标記的對象。

Mark-Sweep 算法是最基礎的收集算法,幾乎所有的收集算法都是基于這種思路并對其不足進行改進而得到。它的不足之處主要有兩個:

  • 效率問題。标記和清除兩個過程的效率都不高;
  • 空間問題。标記清除之後會産生大量的記憶體碎片,空間碎片太多可能會導緻在需要配置設定較大對象時,無法找到足夠的連續記憶體而不得不提前觸發另一次垃圾收集動作。

二、複制算法(Copying)

這種算法将可用記憶體按容量劃分為大小相等的兩塊,每次隻使用其中的一塊。當這一塊記憶體用完了,就将标記存活的對象複制到另外一塊上面,然後再把已使用的半區空間一次性清理掉。

Copying 算法每次都隻對半區進行回收,很好的解決了記憶體碎片的問題,而且實作簡單,運作高效。

該算法的不足之處在于:

  • 将記憶體縮小為原來的一半,代價高昂,并且需要額外空間做配置設定擔保。
  • 在對象存活率較高的時就要進行較多的複制操作,效率降低。

現在的商用虛拟機都采用這種收集算法來回收新生代。虛拟機将新生代記憶體分為一塊較大的 Eden 空間和兩塊較小的 Survivor 空間(預設比例是 8:1:1),每次配置設定對象時,使用 Eden 和其中一塊 Survivor 空間,當進行垃圾回收時,将 Eden 和 Survivor 标記存活的對象複制到另外一塊 Survivor 空間上,然後一次清理掉 Eden 和剛才用過的 Survivor 空間。當然,我們沒有辦法保證每次回收都隻有不多餘 10% 的對象存活,當 Survivor 空間不夠用時,需要依賴其他記憶體(這裡指老年代)進行配置設定擔保。

三、标記-整理算法(Mark-Compact)

這種算法分為“标記”和“整理”兩個階段:首先标記出所有需要回收的對象,然後讓所有存活的對象都向一端移動,接着直接清理掉端邊界以外的記憶體。

Mark-Compact 算法在解決記憶體碎片的同時,避免 Copying 算法的空間浪費和效率問題。

四、分代收集算法(Generational Collection)

這種算法根據對象存活周期的不同将記憶體劃分為幾塊,一般把 Java 堆分為新生代和老生代。

在新生代,每次垃圾收集時都發現有大批對象死去,隻有少量存活,那就選用 Copying 算法,隻要付出少量存活對象的複制成本就可以完成收集。

在老生代,對象存活率高,沒有額外空間對它進行擔保,就必須使用 Mark-Sweep 或者 Mark-Compact 算法。