1. 程式人生 > >GC回收之垃圾收集演算法

GC回收之垃圾收集演算法

    JAVA執行時記憶體區域 

 

一,垃圾收集演算法

GC管理的主區域是Java堆,一般情況下只針對堆進行垃圾回收。方法區,棧,本地方法區不被GC所管理,因而選擇這些區域內的物件作為GC根,被GC根引用的物件不被GC回收。
.GC(垃圾收集器)根,特指的是垃圾收集器的物件,GC會收集那些不是GC根和沒有被GC根引用的物件。

一般的可標記為存活物件的是

1.虛擬機器棧中引用的物件 。
2.本地方法棧中天然方法引用的物件 。
3.方法區中靜態屬性引用的變數 。
4.方法區中常量引用的物件。


演算法分析

1.標記 - 清除演算法(Mark-Sweep)
    應用場景:   針對老年代的CMS收集器;
    思路:
        標記:遍歷獲取所有的GC Roots,然後將所有GC Roots可達的物件標記為存活物件。
        清楚:遍歷堆中所有的物件,將沒有標記的物件全部清除掉。
    優點:
       基於最基礎的可達性分析演算法,它最基礎的收集演算法。
       後續的收集演算法都是基於這種思路並對其不足進行改進的得到。
    缺點:


        效率問題,標記和清除過程的效率都不高。
        空間問題,標記清除之後會產生大量不連續的記憶體碎片【空間碎片太多可能會導致大物件無法分配到足夠的連續記憶體,從而不得不提前觸發GC,甚至停止世界。
    

 

 
2.複製演算法(複製)一般用於新生代
    應用場景:如序列收集器,ParNew收集器,Parallel Scavenge收集器, G1(從區域性看)
    優化標記/ 清除演算法的效率低,記憶體碎片多的問題。
    思路:


        (A)記憶體按容量劃分為大小相等的兩塊,每次使用其中的一塊。
        (B)當一塊記憶體用完了,就將還存活的物件複製到另一塊上(而後使用這一塊)。
        (C)再把已使用過的那塊記憶體空間一次清理掉,而後重複步驟B。
    優點:
        這使得每次都是隻對整個半區進行記憶體回收。
        記憶體分配時也不用考慮記憶體碎片等問題(可使用“指標碰撞”的方式分配記憶體)。
        實現簡單,執行高效。
    缺點:
        空間浪費[將記憶體縮小為原來的一半,浪費了一半的記憶體空間,代價太高【解決:可以改良,不按1:1劃分比例】。
        效率隨物件存活率升高而變低【當物件存活率較高時,需要進行較多複製操作,效率將會變低(解決:後面 標記-整理演算法)】。


 
3,標記 - 整理演算法(Mark-Compact)一般用於老年代
    應用場景:部分一般收集器採用這種演算法來回收老年代,如Serial Old收集器,G1(從整體看)產生。
    :複製演算法在物件存活率較高時就要執行較多的複製操作,效率將會變低。以及50%的空間浪費。
    思路:
        標記:標記過程與“標記 - 清除”演算法一樣;
        整理:讓所有存活的物件都向一端移動,按照記憶體地址次序依次排列,然後直接清理掉端邊界[GCRoot引用到的存活物件]以外的記憶體。【所有現代的標記 - 整理回收器均使用滑動整理】
    優點:
        不會像複製演算法,效率隨物件存活率升高而變低。
        不會像標記 - 清除演算法,產生記憶體碎片。
        可以解決迴圈引用的問題。
        必要時才回收(記憶體不足時) 
    缺點:
        回收時,應用需要掛起,也就是阻止世界(stop-the-world)【停止應用程式,會這導致使用者體驗非常差】
        清理出來的空閒記憶體不是連續 。
        除像標記-清除演算法的標記過程外,還多了需要整理的過程,效率更低。

 

 

4,分代收集演算法[像是排程員]
    應用場景:目前幾乎所有商業虛擬機器的垃圾收集器都採用分代收集演算法。[Serial,Serial Old,ParNew,Parallel Scavenge,Parallel Old,CMS]產生
    結合不同的收集演算法處理不同區域。
    思路:
        根據物件存活週期的不同將記憶體劃分為幾塊。
        根據各個年代的特點採用最適當的收集演算法;
    優點:
        可以根據各個年代的特點採用最適當的收集演算法;
    缺點:
         不能控制還是垃圾每次收集的時間
    補充:
        在新生代中,存活物件少,存活率低於是選用複製演算法,需要只付出少量存活物件的複製成本就可以完成收集
        在老年代中,存活物件多,存活率高。於是選用標記 - 清理“或”標記 - 整理“

三,對比


    效率:複製演算法>標記/整理演算法>標記/清除演算法(標記/清除演算法有記憶體碎片問題,給大物件分配記憶體時可能會再次出發垃圾回收)
    記憶體整齊率:複製演算法=標記/整理演算法>標記/ 清除演算法
    記憶體利用率:標記/整理演算法=標記/清除演算法>複製演算法
    
    
結果:標記/清除演算法已經是比較落後了,不過作為最基礎的收集演算法!還是有地方可以採用的
      目前最好的算是分代收集演算法了!畢竟幾乎所有商業虛擬機器的垃圾收集器都採用了。結合前三個演算法的優點,將演算法組合使用進行垃圾回收!
      分代收集演算法的原理是採用複製演算法來收集新生代,採用標記/清理演算法或者標記/整理演算法收集老年代。

共同點 
    當GC執行緒啟動時(即進行垃圾收集),應用程式都要暫停(stop-the-world

 

參考文件

https://blog.csdn.net/clover_lily/article/details/80160726