JVM 垃圾收集技術研究

風靈使發表於2019-04-06

GCTree

JVM 垃圾收集技術研究

根搜尋演算法

在這裡插入圖片描述

物件存活判斷
         1)引用計數法
            每個物件有一個引用的計數屬性,新增一個引用時計數加1,引用釋放的時候計數減一,
         計數為0的時候可以回收,此方法簡單,但是無法解決物件的相互迴圈引用的問題。

         2)根搜尋演算法
            也叫可達性分析演算法。
            從GC Roots開始向下搜尋,搜尋所走過的路徑稱為引用鏈。當一個物件到GC Roots沒有
         任何引用鏈時,則證明這個物件是不可用的。不可達物件。

            在Java中,GC Roots包括:
                                  1)虛擬機器棧中引用的物件。
                                  2)方法區中類靜態屬性實體引用的物件。
                                  3)方法區中常量引用的物件。
                                  5)本地方法棧中JNI引用的物件。
無論通過引用計數法判斷物件的引用數量,還是通過根搜尋演算法判斷物件的引用鏈是否可達,判斷物件
是否存活都與“引用”有關。

      Java中將引用分為
           1)強引用
              就是在程式程式碼中普遍存在的,例如Object obj = new Object();
              只要強引用還在,垃圾收集器就永遠不會回收。

           2)軟引用
              軟引用用來描述一些還有用,但並不是必須的物件。對於軟引用關聯著的物件,在系統
           將要發生記憶體溢位之前,將會把這些物件列進回收範圍之中並進行第二次回收,如果這次
           回收還是沒有足夠的記憶體,才會跑出記憶體溢位。

           3)弱引用
              弱引用也是用來描述非必須物件的,但是它的強度比弱引用更弱一些,被弱引用關聯
           的物件只能生存到下一次垃圾收集發生之前,當垃圾收集器工作時,無論當前記憶體是否
           足夠,都會回收掉弱引用關聯的物件。

           5)虛引用
              虛引用也稱為幽靈引用,它是最弱的一種引用,一個物件是否有虛引用的存在,完全不
           會對其生存環境構成影響,為一個物件設定虛引用關聯的唯一目的就是希望能在這個物件
           被收集器回收的時候收到一個系統通知。

垃圾收集演算法

在這裡插入圖片描述

標記清除演算法

     優點:基於最基礎的可達性分析演算法,它是最基礎的收集演算法。
     缺點:
         1)效率問題
            標記和清除兩個過程的效率都不高。
         2)空間問題
            這會導致分配大記憶體物件時,無法找到足夠的連續記憶體,從而提前觸發一次垃圾收集。

在這裡插入圖片描述

標記複製演算法

     思路:
          1)把記憶體劃分為大小相等的兩塊,每次只使用其中一塊。
          2)當一塊記憶體用完了,就將還存貨的額物件複製到另一塊上,而後使用這一塊。
          3)再把已使用過的那塊記憶體空間一次清理掉,而後重複步驟2

     優點:
         1)這使得每次都是隻對半個區進行記憶體回收。
         2)記憶體分配時不用考慮記憶體碎片問題。
         3)實現簡單,執行高效。

     缺點:
         1)空間浪費。
         2)效率隨物件存活率升高而變低
            當物件存活率較高時,需要進行較多的複製操作,效率將會變低。

在這裡插入圖片描述

標記整理演算法

      步驟:
          1)標記階段
             標記階段與標記清除演算法一樣。
          2)整理
             但後續不是直接對可回收物件進行清理,而是讓所有存活的物件都向一端移動;
             然後直接清理掉端邊界意外的記憶體。

      優點:
          1)效率隨存活物件升高而降低。
          2)不會產生記憶體碎片。   
分代收集演算法

      演算法思路:
 
              根據物件存活週期的不同將記憶體劃分成幾塊;
              這樣可以根據各個年代的特點進行最適當的手機演算法
    
      1)新生代:
         每次垃圾收集都有大批物件死去,只有少量存活。
         採用標記複製演算法。

      2)老年代:
         物件存活率高,沒有額外的空間可以分配擔保
         採用標記清理演算法或者標記整理演算法。

垃圾收集器

Serial 序列收集器

          採用標記複製演算法
          單執行緒,只會使用一個CPU或一條執行緒去完成垃圾收集工作。
          另一方面也意味著它進行垃圾收集時,必須暫定其他所有的工作執行緒,知道它收集結束為止
      這個過程也稱為Stop The World。

          應用:
              桌面應用場景。單個CPU等。

在這裡插入圖片描述

ParNew 並行收集器

           Serial收集器的多執行緒版本;
           是許多執行在Server模式下的虛擬機器中首選的新生代收集器,很重要的原因是:除
       了Serial收集器外,只有ParNew收集器能與CMS收集器配合工作。它預設開啟你的手機執行緒與
        CPU資料相同。
Parallel Scavenge 並行清除收集器

           新生代手機演算法,使用標記複製演算法,也是並行的多執行緒收集器。

           Parallel Scavenge收集器的目標是達到一個可控制的吞吐量。
           CMS收集器關注的是儘可能縮短垃圾收集使用者執行緒的停頓時間。

           與ParNew收集器的重要區別就是它具有自適應調節策略

      應用場景:
           Parallel Scavenge收集器是虛擬機器執行在Server模式下的預設垃圾收集器。
           停頓時間短適合需要與使用者互動的程式,良好的響應速度能提升使用者體驗;高吞吐量則可以
      高效率的利用CPU時間,儘快完成運算任務,主要適合在後臺運算而不需要太多互動的任務。

           該收集器以高吞吐量為目標,就是減少垃圾收集時間,從而讓使用者程式碼獲得更長的執行
      時間,所以適合執行在多個CPU上,並且專注於後臺計算的應用程式,例如:批量執行處理任務,
      訂單處理,工資支付,科學計算等。

在這裡插入圖片描述

Serial Old收集器

            Serial收集器的老年版本,它同樣是一個單執行緒收集器,使用標記-整理演算法。

在這裡插入圖片描述

Parallel Old收集器

            Parallel收集器的老年代版本,使用多執行緒和標記整理演算法

在這裡插入圖片描述

CMS收集器

            CMS(Concurrent Mark Sweep)收集器:基於標記清除演算法實現,不會進行壓縮,會
      產生記憶體碎片,特點是:併發收集,低停頓。

      應用場景:
              與使用者互動多的場景。
              CMS收集器是一種以獲得最短回收停頓時間為目標的收集器。

      運作過程:
              1)初始標記
                 初始標記僅僅只是標記一下GC ROOTS能直接關聯到的物件,速度很快,但需要Stop The World;
              2) 併發標記
                 併發標記階段即使進行GC ROOTS Tracing的過程,剛才產生的集合中標記出存活
              物件;應用程式也在執行,並不能保證可以標記處所有的存活物件。
              3)重新標記
                 為了修正在併發標記期間因使用者程式執行而導致標記產生變動的那一部分物件的標
              記記錄,然然需要Stop The World,這個階段的停頓時間一般會比初始標記階段
              稍長一些,但遠比並發標記的實際端。
              5)併發清除
                 併發清除階段會清除物件,回收所有的垃圾物件。

      缺點:
       
               CMS收集器對CPU資源非常敏感。
               CMS預設啟動的執行緒數 = (CPU數量 + 3) / 4,當CPU不足4個時,CMS收集器對使用者
           的影響就可能變得更大,可能會無法接受
            
               CMS收集器無法處理浮動垃圾
               
           浮動垃圾:
               由於CMS在併發清理階段使用者執行緒還在執行著,伴隨程式自然就會有新的垃圾不斷產生
           ,這一部分垃圾出現在標記過程之後,CMS無法在檔次垃圾中處理掉他們,只好留待下一次
           GC是在清理掉。

           由於在垃圾收集階段使用者執行緒還需要執行,那就需要預留足夠的記憶體空間給使用者執行緒,因此
           CMS收集器不能像其他收集器那樣等到老年代幾乎完全被填滿了才進行垃圾收集,也可以認
           為CMS所需要的記憶體空間比其他垃圾收集器要大:
       
               CMS收集器會產生大量的記憶體碎片
               CMS收集器是一款基於標記,清除演算法實現的收集器,清除後不進行壓縮操作,這意味
           著收集結束時會有大量的空間碎片產生。空間碎片過多時將會給大物件分配帶來很大麻煩,
           往往會出現老年代還有很大的空間剩餘,但是無法找到足夠大的連續空間來分配當前物件,
           不得不提前觸發一次Full Gc.

               解決辦法:
          
               (1)、"-XX:+UseCMSCompactAtFullCollection"
                   使得CMS出現上面這種情況時不進行Full GC,而開啟記憶體碎片的合併整理
                   過程; 但合併整理過程無法併發,停頓時間會變長; 預設開啟(但不會進
                   行,結合下面的CMSFullGCsBeforeCompaction);    
               (2)、"-XX:+CMSFullGCsBeforeCompaction"
                   設定執行多少次不壓縮的Full GC後,來一次壓縮整理; 為減少合併整理過
                   程的停頓時間; 預設為0,也就是說每次都執行Full GC,不會進行壓縮整理;
G1 Garbage First收集器
觸發GC的時機:

      1)年輕代或者老年代慢了,JAVA虛擬機器無法再為新的物件分配記憶體空間了,那麼Java虛擬機器就
         會觸發一次GC去回收掉那些已經不會再被使用到的物件。
      2)手動呼叫System.gc()方法,通常這樣會觸發一次Full GC以及至少一次Minor GC.
JVM記憶體分配策略
GC Roots迴圈引用
 
      可達性分析演算法
FullGC的原因猜測:

      1)CMSGC失敗導致
      2)大物件分配時,空間不夠導致
      3)記憶體碎片導致

      設定列印FullGC的原因配置
          jinfo -flag +PrintGCReason

      總結:
          1,問題可能出現的原因,要儘快動手去驗證,不要只停留在思考的層面;
          2,出現fullgc的時候,可以通過加上PrintGCReason,檢視具體GC原因。

相關文章