1. 程式人生 > >Jvm垃圾回收器(終結篇) Jvm垃圾回收器(基礎篇) Jvm垃圾回收器(演算法篇)

Jvm垃圾回收器(終結篇) Jvm垃圾回收器(基礎篇) Jvm垃圾回收器(演算法篇)

Jvm垃圾回收目前就準備了這三篇博文進行整理,在寫博文的過程中我也是邊看邊記載的,我覺得這種學習方式更容易讓人記住,不會輕易忘記。以前的學習模式都是看PDF文件、看書等,但是有個缺點就是當時記住了過段時間就會忘記,因此想把學習過程中重要的部分做個筆記總結,以便於後期複習回顧(學習技巧僅個人觀點)同時也希望lz的部落格能幫助到廣大園友一丟丟。在此立個Flag!以後我會堅持寫部落格的。哈哈--好了 接下來言歸正傳。

知識回顧:

第一篇《Jvm垃圾回收器(基礎篇)》主要講述了判斷物件的生死?兩種基礎判斷物件生死的演算法、引用計數法、可達性分析演算法,方法區的回收。在第二篇《Jvm垃圾回收器(演算法篇)

》中主要介紹了垃圾回收的幾種常用演算法:標記-清除、複製演算法、標記-整理演算法、分代收集算法。那麼接下來我們重點研究Jvm的垃圾收集器(serial收集器、parnew收集器、parallel scavenge收集器、serial  old 收集器、parallel old收集器、cms收集器、g1收集器)。前面說了那麼多就是為它做鋪墊的。

正式進入前先看下圖解HotSpot虛擬機器所包含的收集器:

圖中展示了7種作用於不同分代的收集器,如果兩個收集器之間存在連線,則說明它們可以搭配使用。虛擬機器所處的區域則表示它是屬於新生代還是老年代收集器

新生代收集器:Serial、ParNew、Parallel Scavenge

老年代收集器:CMS、Serial Old、Parallel Old

整堆收集器: G1

幾個相關概念:

並行收集:指多條垃圾收集執行緒並行工作,但此時使用者執行緒仍處於等待狀態。

併發收集:指使用者執行緒與垃圾收集執行緒同時工作(不一定是並行的可能會交替執行)。使用者程式在繼續執行,而垃圾收集程式執行在另一個CPU上。

吞吐量:即CPU用於執行使用者程式碼的時間與CPU總消耗時間的比值(吞吐量 = 執行使用者程式碼時間 / ( 執行使用者程式碼時間 + 垃圾收集時間 ))。例如:虛擬機器共執行100分鐘,垃圾收集器花掉1分鐘,那麼吞吐量就是99%

一:Serial 收集器

Serial收集器是最基本的、發展歷史最悠久的收集器。

特點:單執行緒、簡單高效(與其他收集器的單執行緒相比),對於限定單個CPU的環境來說,Serial收集器由於沒有執行緒互動的開銷,專心做垃圾收集自然可以獲得最高的單執行緒手機效率。收集器進行垃圾回收時,必須暫停其他所有的工作執行緒,直到它結束(Stop The World)。

應用場景:適用於Client模式下的虛擬機器。

Serial / Serial Old收集器執行示意圖

 

二:ParNew收集器

ParNew收集器其實就是Serial收集器的多執行緒版本。

除了使用多執行緒外其餘行為均和Serial收集器一模一樣(引數控制、收集演算法、Stop The World、物件分配規則、回收策略等)。

特點:多執行緒、ParNew收集器預設開啟的收集執行緒數與CPU的數量相同,在CPU非常多的環境中,可以使用-XX:ParallelGCThreads引數來限制垃圾收集的執行緒數。

   和Serial收集器一樣存在Stop The World問題

應用場景:ParNew收集器是許多執行在Server模式下的虛擬機器中首選的新生代收集器,因為它是除了Serial收集器外,唯一一個能與CMS收集器配合工作的

ParNew/Serial Old組合收集器執行示意圖如下:

 

 

三:Parallel Scavenge 收集器

與吞吐量關係密切,故也稱為吞吐量優先收集器。

特點:屬於新生代收集器也是採用複製演算法的收集器,又是並行的多執行緒收集器(與ParNew收集器類似)。

該收集器的目標是達到一個可控制的吞吐量。還有一個值得關注的點是:GC自適應調節策略(與ParNew收集器最重要的一個區別)

GC自適應調節策略:Parallel Scavenge收集器可設定-XX:+UseAdptiveSizePolicy引數。當開關開啟時不需要手動指定新生代的大小(-Xmn)、Eden與Survivor區的比例(-XX:SurvivorRation)、晉升老年代的物件年齡(-XX:PretenureSizeThreshold)等,虛擬機器會根據系統的執行狀況收集效能監控資訊,動態設定這些引數以提供最優的停頓時間和最高的吞吐量,這種調節方式稱為GC的自適應調節策略。

Parallel Scavenge收集器使用兩個引數控制吞吐量:

  • XX:MaxGCPauseMillis 控制最大的垃圾收集停頓時間

  • XX:GCRatio 直接設定吞吐量的大小。

四:Serial Old 收集器

Serial Old是Serial收集器的老年代版本。

特點:同樣是單執行緒收集器,採用標記-整理演算法。

應用場景:主要也是使用在Client模式下的虛擬機器中。也可在Server模式下使用。

Server模式下主要的兩大用途(在後續中詳細講解···):

  1. 在JDK1.5以及以前的版本中與Parallel Scavenge收集器搭配使用。
  2. 作為CMS收集器的後備方案,在併發收集Concurent Mode Failure時使用。

Serial / Serial Old收集器工作過程圖(Serial收集器圖示相同):

五:Parallel Old 收集器

是Parallel Scavenge收集器的老年代版本。

特點:多執行緒,採用標記-整理演算法。

應用場景:注重高吞吐量以及CPU資源敏感的場合,都可以優先考慮Parallel Scavenge+Parallel Old 收集器。

Parallel Scavenge/Parallel Old收集器工作過程圖:

六:CMS收集器

一種以獲取最短回收停頓時間為目標的收集器。

特點:基於標記-清除演算法實現。併發收集、低停頓。

應用場景:適用於注重服務的響應速度,希望系統停頓時間最短,給使用者帶來更好的體驗等場景下。如web程式、b/s服務。

CMS收集器的執行過程分為下列4步:

初始標記:標記GC Roots能直接到的物件。速度很快但是仍存在Stop The World問題。

併發標記:進行GC Roots Tracing 的過程,找出存活物件且使用者執行緒可併發執行。

重新標記:為了修正併發標記期間因使用者程式繼續執行而導致標記產生變動的那一部分物件的標記記錄。仍然存在Stop The World問題。

併發清除:對標記的物件進行清除回收。

 CMS收集器的記憶體回收過程是與使用者執行緒一起併發執行的。

 CMS收集器的工作過程圖:

CMS收集器的缺點:

  • 對CPU資源非常敏感。
  • 無法處理浮動垃圾,可能出現Concurrent Model Failure失敗而導致另一次Full GC的產生。
  • 因為採用標記-清除演算法所以會存在空間碎片的問題,導致大物件無法分配空間,不得不提前觸發一次Full GC。

七:G1收集器

一款面向服務端應用的垃圾收集器。

特點如下:

並行與併發:G1能充分利用多CPU、多核環境下的硬體優勢,使用多個CPU來縮短Stop-The-World停頓時間。部分收集器原本需要停頓Java執行緒來執行GC動作,G1收集器仍然可以通過併發的方式讓Java程式繼續執行。

分代收集:G1能夠獨自管理整個Java堆,並且採用不同的方式去處理新建立的物件和已經存活了一段時間、熬過多次GC的舊物件以獲取更好的收集效果。

空間整合:G1運作期間不會產生空間碎片,收集後能提供規整的可用記憶體。

可預測的停頓:G1除了追求低停頓外,還能建立可預測的停頓時間模型。能讓使用者明確指定在一個長度為M毫秒的時間段內,消耗在垃圾收集上的時間不得超過N毫秒。

G1為什麼能建立可預測的停頓時間模型?

因為它有計劃的避免在整個Java堆中進行全區域的垃圾收集。G1跟蹤各個Region裡面的垃圾堆積的大小,在後臺維護一個優先列表,每次根據允許的收集時間,優先回收價值最大的Region。這樣就保證了在有限的時間內可以獲取儘可能高的收集效率。

G1與其他收集器的區別

其他收集器的工作範圍是整個新生代或者老年代、G1收集器的工作範圍是整個Java堆。在使用G1收集器時,它將整個Java堆劃分為多個大小相等的獨立區域(Region)。雖然也保留了新生代、老年代的概念,但新生代和老年代不再是相互隔離的,他們都是一部分Region(不需要連續)的集合。

G1收集器存在的問題:

Region不可能是孤立的,分配在Region中的物件可以與Java堆中的任意物件發生引用關係。在採用可達性分析演算法來判斷物件是否存活時,得掃描整個Java堆才能保證準確性。其他收集器也存在這種問題(G1更加突出而已)。會導致Minor GC效率下降。

G1收集器是如何解決上述問題的?

採用Remembered Set來避免整堆掃描。G1中每個Region都有一個與之對應的Remembered Set,虛擬機發現程式在對Reference型別進行寫操作時,會產生一個Write Barrier暫時中斷寫操作,檢查Reference引用物件是否處於多個Region中(即檢查老年代中是否引用了新生代中的物件),如果是,便通過CardTable把相關引用資訊記錄到被引用物件所屬的Region的Remembered Set中。當進行記憶體回收時,在GC根節點的列舉範圍中加入Remembered Set即可保證不對全堆進行掃描也不會有遺漏。

如果不計算維護 Remembered Set 的操作,G1收集器大致可分為如下步驟:

初始標記:僅標記GC Roots能直接到的物件,並且修改TAMS(Next Top at Mark Start)的值,讓下一階段使用者程式併發執行時,能在正確可用的Region中建立新物件。(需要執行緒停頓,但耗時很短。)

併發標記:從GC Roots開始對堆中物件進行可達性分析,找出存活物件。(耗時較長,但可與使用者程式併發執行)

最終標記:為了修正在併發標記期間因使用者程式執行而導致標記產生變化的那一部分標記記錄。且物件的變化記錄線上程Remembered Set  Logs裡面,把Remembered Set  Logs裡面的資料合併到Remembered Set中。(需要執行緒停頓,但可並行執行。)

篩選回收:對各個Region的回收價值和成本進行排序,根據使用者所期望的GC停頓時間來制定回收計劃。(可併發執行)

G1收集器執行示意圖:

 

 

 

 

-- 結束-- JVM垃圾收集暫告一段落。