java學習筆記-4 JVM垃圾回收(GC)

sunkang發表於2018-06-24

引言

jvm垃圾回收相關的問題是老生常談的問題了,相信大家都有所瞭解,這裡再進行相關的探討,以加深理解。若文中有不正之言,望不吝指正。

本文將圍繞以下幾個點展開

1.為什麼要進行垃圾回收

我們知道jvm的記憶體結構中,依賴記憶體是否可共享,將記憶體劃分為執行緒專享區和執行緒共享區,其中程式計數器、虛擬機器棧、本地方法棧作為執行緒獨享的記憶體,生命週期跟執行緒相關,比如棧中的棧幀需要分配多少記憶體一般在類結構確定時就已經確定好了,執行緒結束時記憶體就跟著回收了,不需要我們過多的關心垃圾回收。但是作為執行緒共享的記憶體區域,堆和方法區,java new出來的物件大部分是在堆中分配的,而且只有在程式執行期間才能知道會建立出哪些物件,在這部分記憶體的分配和回收是我們關心的。其實歸根結底就是一句話,知道底層的jvm怎麼進行垃圾回收的,對我們排查記憶體溢位和洩露的問題有很大幫助,而且對於提高程式的效能也有很大幫助。
複製程式碼

2.哪些記憶體需要回收

 判斷哪些記憶體需要回收有兩種經典的演算法
  1.引用計數器法
     引用計數器法的實現是給物件新增一個引用計數器,當物件被引用時,計數器值就+1,物件引用失效時計數器值-1,當引用計數器值為0時,可認為物件無引用,此時可被回收。但是這個演算法不能解決物件迴圈引用的問題,所以虛擬機器並不是採用這種方式去判斷是否回收物件的。
  2.可達性分析演算法
    可達性分析演算法是將可作為GC Roots的物件作為起始點,展開搜尋,搜尋所經過的路徑,對一個物件到GC Roots 沒有任何引用,則意味著物件不可達,此時物件可被回收。
複製程式碼

java學習筆記-4 JVM垃圾回收(GC)
其中的GCRoots,其實就是一組必須活躍的引用,大致包括以下幾種

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

方法區垃圾回收:其實方法區中也是可以進行垃圾回收的,只是回收的價效比很低,就是說執行一次垃圾回收的動作並不能回收到很多的空間。但是滿足一定條件還是可以進行回收的,一般回收的是無用的類和廢棄常量。其中判斷常量是否廢棄,沒有引用即可;而判斷類無用需滿足 1.java堆中沒有任何該類的例項;2.載入該類的類載入器已被回收;3.該類的java.lang.Class物件沒有在任何地方被引用 ,不能通過反射獲取該類的方法。

3.何時進行垃圾回收

 即觸發GC的時間,在新生代的Eden區滿了,會觸發新生代GC(MinorGC),經過多次觸發新生代GC存活下來的物件就會升級到老年代,升級到老年代的物件所需的記憶體大於老年代剩餘的記憶體,則會觸發老年代GC(FullGC)。當程式呼叫System.gc()時也會觸發Full GC。
複製程式碼

4. 垃圾收集演算法

 1. 標記清除演算法
    見名知意,該演算法分為兩個階段,標記和清除。如下圖所示,標記就是根據之前的GCRoots判斷物件是否還有引用可達。但是這個演算法的缺點也很明顯,第一就是標記和清除效率都很低;第二就是這種演算法會造成大量不連續的記憶體碎片的存在,當程式需要分配大的記憶體空間給物件的時候,很可能因為無法找到連續的大的記憶體空間,再一次觸發GC。圖如下:
複製程式碼

java學習筆記-4 JVM垃圾回收(GC)
圖中物件b沒有引用,會被回收
java學習筆記-4 JVM垃圾回收(GC)
圖中可以看出,回收後有大量不連續的記憶體空間。

 2.複製演算法
    基於上一個演算法,為了解決效率問題,引申出“複製”演算法。基本思想就是將記憶體劃分為大小相等的兩塊,每次只使用其中的一塊,當一塊用完將整個活著的物件全部複製到另一個半區,此時不需要考慮記憶體碎片的問題,只需移動指標即可,簡單高效。但是需要浪費一半的記憶體。由於java堆記憶體物件存活的特點,大部分新生代中的物件存活時間都比較短,所以主流的虛擬機器會將記憶體分為一塊較大的Eden區和兩塊較小的Survivor區,比例是8:1:1。每次分配物件到Eden區和一個Suivivor區,垃圾回收時將整個Eden區和剛才用到的Survivor區存活的物件一次性“複製”到未被使用的那個Survivor區,最後清理掉Eden區和之前的分配的Survivor區。但是並不能保證在將Eden去和Survivor區複製到另一個Survivor區的時候記憶體空間一定是充足的,此時需要依賴其它記憶體進行分配擔保。分配擔保策略指明,當Eden區和之前的Survivor區之存活的物件複製到另一塊Survivor區時,記憶體空間若不夠,則直接將記憶體分配到老年代。
複製程式碼

java學習筆記-4 JVM垃圾回收(GC)
複製的時候只需移動相應的指標即可
java學習筆記-4 JVM垃圾回收(GC)

 3.標記整理演算法
     從上面的“複製”演算法可以得知,當物件的存活率很高時,進行復制操作的時候效率將會變低。這時有人提出標記整理演算法,標記過程較之前相同,只是後面不直接對物件進行清除,而是將存活的物件都向一邊移動,並更改對應的指標,然後清理掉右端以外的記憶體。但是這個演算法效率也不高因為,因為包括了標記 + 移動,但是也解決了記憶體碎片的問題。
複製程式碼

java學習筆記-4 JVM垃圾回收(GC)

java學習筆記-4 JVM垃圾回收(GC)
4.分代收集演算法

    其實就是上面演算法的綜合。根據物件的存活週期,一般把java堆分為新生代和老生代。新生代中物件存活率較低,選用複製演算法,只需付出少量存活物件的複製成本即可完成收集;老年代中物件存活率高,沒有額外的內控空間進行擔保,必須使用標記清除或者標記整理演算法來進行垃圾回收。
複製程式碼

5.幾種垃圾收集器比較

在進行比較之前先介紹下衡量的引數

  1. 吞吐量: 所謂吞吐量就是 CPU 用於執行程式碼的時間與 CPU 總消耗時間的比值,即吞吐量 = 執行使用者程式碼時間 / (執行使用者程式碼時間 + 垃圾收集時間)

  2. 停頓時間 :jvm在執行垃圾回收時,需停頓使用者程式,這裡指的是停頓使用者程式的時常

java學習筆記-4 JVM垃圾回收(GC)

Serial收集器

單執行緒的收集器,採用 複製演算法,進行垃圾回收時需要將使用者的所有執行緒全部暫停直到垃圾回收動作的完成。互動體驗不是很好,但是在單核cpu或者虛擬機器執行在client模式下,停頓時間可以控制到毫秒級,是可以接受。
複製程式碼

Serial Old收集器

 Serial Old是Serial的老年代版本,使用   標記整理演算法 
複製程式碼

java學習筆記-4 JVM垃圾回收(GC)

ParNew收集器

 ParNew收集器採用複製演算法,其實就是Serial收集器的多執行緒版本,是server模式下首選的新生代收集器。在多核cpu下有著比Serial更好的表現。
複製程式碼

java學習筆記-4 JVM垃圾回收(GC)

Parallel Scavenge收集器

 採用複製演算法,也是一個新生代的收集器。較於ParNew收集器,Parallel Scavenge收集器更多的目標是達到一個可控制的吞吐量。Parallel Scavenge收集器提供了兩個引數用於精確控制吞吐量,分別是控制最大垃圾收集 停頓時間的-XX:MaxGCPauseMillis引數以及直接設定吞吐量大小的-XX:GCTimeRatio參 數
複製程式碼

Parallel Old收集器

 Parallel Old收集器採用  標記整理演算法 是ParallelScavenge收集器的老年代版本。在注重吞吐量以及cpu資源敏感的場合,都可以優先考慮Parallel Scavenge加Parallel Old收集器。
複製程式碼

java學習筆記-4 JVM垃圾回收(GC)

CMS(Concurrent Mark Sweep)收集器

 採用標記清除演算法,是一個老年代的並行收集器,是一個以獲取最短回收停頓時間為目標的處理器,具有高併發、低停頓的特點。包括四個過程:初始標記、併發標記、重新標記、併發清除。其中初始標記和重新標記都需要暫停使用者所有執行緒。
複製程式碼

java學習筆記-4 JVM垃圾回收(GC)
但是這個收集器也有明顯的缺點, 1. 前面介紹過的標記清除演算法講到會產生大量的記憶體碎片,很可能老年代空間有富餘,但是新生代沒有足夠大的記憶體空間分配物件。CMS收集器提供了一個-XX:+UseCMSCompactAtFullCollection開 關引數(預設就是開啟的),用於在CMS收集器頂不住要進行FullGC時開啟記憶體碎片的合併 整理過程,記憶體整理的過程是無法併發的,空間碎片問題沒有了,但停頓時間不得不變長; 2.CMS收集器無法處理浮動垃圾(由於cms併發清理階段使用者執行緒還在執行,這段時間內出現的新的垃圾稱之為浮動垃圾) 3.cms對cpu的資源很敏感,雖然收集器是併發的,但是因為佔用了一部分執行緒會導致應用程式變慢,總吞吐量降低。

G1收集器

G1收集器基於 標記整理演算法 是面向服務端的,是面向新生代和老生代的收集器。收集過程主要包括:1.初始標記:標記GCRoots能關聯的物件;
2.併發標記:從GC Roots開始對物件進行可達性分析,找出存活的物件,耗時較久,可與使用者執行緒同時進行 3.最終標記:修正在併發標記期間因使用者程式繼續運作而導致標記產生變動的那一部分標記記錄 4.篩選回收:首先對各個region的回收價值和成本進行排序,根據使用者所期望的停頓時間來制定回收計劃,優先處理最需要回收的。 
複製程式碼

java學習筆記-4 JVM垃圾回收(GC)

收集器引數總結

java學習筆記-4 JVM垃圾回收(GC)

引用

《深入理解java虛擬機器》 周志明

相關文章