1. 程式人生 > >Jvm垃圾回收器(終結篇)

Jvm垃圾回收器(終結篇)

掃描 star rem 提前 判斷 清除 png 重要 處的

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垃圾收集暫告一段落。

Jvm垃圾回收器(終結篇)