1. 程式人生 > 其它 >常見的幾種垃圾回收演算法

常見的幾種垃圾回收演算法

垃圾收集演算法

常見的垃圾收集演算法包括:

  • 標記-清除演算法
  • 複製演算法
  • 標記-整理演算法
  • 分代收集演算法

JVM 的垃圾收集演算法是使用了分代收集演算法,複製演算法、標記-整理演算法。三種演算法都有使用。使用分代收集演算法,將 JVM 中的記憶體分為新生代和老年代,新生代採用複製演算法收集,而老年代採用的是標記-整理演算法。

1 標記-清除演算法

標記-清除演算法分為“標記”和“清除”兩個階段,首先通過可達性分析,標記出所有需要回收的物件,然後統一回收所有被標記的物件。

標記-清除演算法有兩個缺陷,一個是效率問題,標記和清除的過程效率都不高,另外一個就是,清除結束後會造成大量的碎片空間。有可能會造成在申請大塊記憶體的時候因為沒有足夠的連續空間導致再次 GC。

2 複製演算法

為了解決碎片空間的問題,出現了“複製演算法”。複製演算法的原理是,將記憶體分成兩塊,每次申請記憶體時都使用其中的一塊,當記憶體不夠時,將這一塊記憶體中所有存活的複製到另一塊上。然後將然後再把已使用的記憶體整個清理掉。java培訓

複製演算法解決了空間碎片的問題。但是也帶來了新的問題。因為每次在申請記憶體時,都只能使用一半的記憶體空間。記憶體利用率嚴重不足。

JVM 中新生代採用的就是複製演算法進行的GC。針對記憶體利用率不足的問題做了一些優化。

IBM公司的專門研究表明,新生代中的物件 98% 是“朝生夕死”的,意思是說,在新生代中,經過一次 GC 之後能夠存活下來的物件僅有 2% 左右。

所以並不需要按照1:1的比例劃分出兩塊記憶體空間。而是將記憶體劃分出三塊,一塊較大的 Eden 區,和兩塊較小的 Survivor 區。其中 Eden 區佔 80% 的記憶體,兩塊 Survivor 各佔 10% 的記憶體。在建立新的物件時,只使用 Eden 區和其中的一塊 Survivor 區,當進行 GC 時,把 Eden 區和 Survivor 區存活的物件全部複製到另一塊 Survivor 區中,然後清理掉 Eden 區和剛剛用過的 Survivor 區。

這種記憶體的劃分方式就解決了記憶體利用率的問題,每次在建立物件時,可用的記憶體為 90%(80% + 10%) 當前記憶體容量。

3 標記-整理演算法

複製演算法在 GC 之後存活物件較少的情況下效率比較高,但如果存活物件比較多時,會執行較多的複製操作,效率就會下降。而老年代的物件在 GC 之後的存活率就比較高,所以就有人提出了“標記-整理演算法”。

標記-整理演算法的“標記”過程與“標記-清除演算法”的標記過程一致,但標記之後不會直接清理。而是將所有存活物件都移動到記憶體的一端。移動結束後直接清理掉剩餘部分。

4 分代收集演算法

分代收集是將記憶體劃分成了新生代和老年代。分配的依據是物件的生存週期,或者說經歷過的 GC 次數。物件建立時,一般在新生代申請記憶體,當經歷一次 GC 之後如果對還存活,那麼物件的年齡 +1。當年齡超過一定值(預設是 15,可以通過引數 -XX:MaxTenuringThreshold 來設定)後,如果物件還存活,那麼該物件會進入老年代。