1. 程式人生 > >JVM - 常見垃圾收集演算法以及原理

JVM - 常見垃圾收集演算法以及原理

標記 - 清除演算法:該演算法分為標記和清除兩個階段,首先標記出所有需要回收的物件,在標記完成後統一回收所有被標記的物件。

缺點:1. 效率問題,標記和清除的效率都很低;2. 空間問題,標記清除之後會產生大量不連續的記憶體碎片,空間碎片太多可能導致以後在程式執行過程中需要分配較大物件時,無法找到足夠的連續記憶體而不得不提前觸發另一次垃圾收集動作。造成系統負擔。

標記-清除的演算法執行過程如下(excel是個比較強大的東西):

複製演算法:該演算法針對Java堆新生代記憶體設計。將可用記憶體按照容量劃分為大小相等的兩塊,每次只使用其中的一塊。當這一塊的記憶體使用完了,就將還存活著的物件複製到另一塊上面,然後再把已使用過的記憶體空間一次清理掉。

優點:由於是對整個半區記憶體回收,沒有記憶體碎片的問題,記憶體再次分配按照順序即可,簡單高效。

缺點:將可用記憶體變為原來一半。

複製演算法的執行流程如下:

這裡有必要解說下,我看書的時候對這張圖片是有點疑惑的,【對於每次只使用一半清理一半】(理解力不好見諒),回收前的狀態是使用左邊一半,右邊作為保留區域,回收時將左邊的還存活的物件複製到右邊的保留區域。然後針對左邊進行回收。下次的執行過程是反過來的,即先把右邊還存活的物件複製到左邊,然後針對右邊一半的記憶體進行回收。這裡囉嗦下再附上一張圖片

現在的商業虛擬機器都是採用這種垃圾收集演算法來回收新生代,但是他們記憶體使用的劃分比例不是按照1:1來劃分的,而是將記憶體分為一塊較大的Eden空間和兩塊較小的Survivor空間,每次使用Eden和其中一塊Survivor。當回收時,將Eden和Survivor中還存活著的物件一次性的複製到另外一塊Survivor空間上,最後清理掉Eden和剛才使用過的Survivor空間。HotSpot虛擬機器預設Eden和Survivor的大小比例是8:1,也就是每次新生代中可用的記憶體空間為整個新生代容量的90%(Eden+Survivor[一塊]),只有10%的記憶體會被浪費。樣的話會有個問題?假如某次回收之後存活的物件超過了那10%大小的記憶體,即記憶體不夠用了,就會發生問題。這裡採用由老年代進行分配擔保來解決這個問題,即將某些物件進入老年代。這裡也貼張圖片

標記-整理演算法:該演算法主要是針對java堆老年代的的物件特點來設計。標記過程與“標記-清除”演算法的標記過程相同,但是後續步驟不是直接對可回收的物件進行清理,而是讓所有存活的物件都向一端移動,然後直接清理掉端邊界以外的記憶體。示意圖如下:

分代收集演算法:根據物件存活週期的不同將記憶體劃分為幾塊,一般是把Java堆分為新生代和老年代,這樣根據各個年代的特點採用最合適的演算法。新生代物件存活率比較低,採用複製演算法。來年代物件存活率高就使用標記-清理或者標記-整理演算法來進行回收。