JVM_垃圾收集器

  最近剛好有時間,就簡單的看了下JVM的幾種垃圾回收器,它們都是計算機歷史發展的產物,先簡單的做一個整理,並沒有哪一款垃圾收集器就一定是最優,還需要結合使用場景、參數配置等進行考量,根據系統情況搭配出儘可能合理優質的垃圾回收策略,而這往往需要經驗的積累;


  如果說收集算法是內存回收的方法論垃圾收集器就是內存回收的具體實現

  下圖展示了7種作用於不同分代的收集器,如果兩個收集器之間存在連線,就說明它們可以搭配使用。

 

併發與並行

  這兩個名詞都是併發編程中的概念,在談論垃圾收集器的上下文語境中,它們可以解釋如下。

並行(Parallel):指多條垃圾收集線程並行工作,但此時用戶線程仍然處於等待狀態

併發(Concurrent):指用戶線程與垃圾收集線程同時執行(但不一定是並行的,可能會交替執行),用戶程序在繼續運行,而垃圾收集程序運行於另一個CPU上。

Serial收集器  [ˈsɪəriəl]

  Serial 收集器是最基本、歷史最悠久的收集器,曾經(在JDK 1.3.1之前)是虛擬機年輕代收集的唯一選擇。

 

特性

  年輕代單線程收集器,但它的“單線程”的意義並不僅僅說明它只會使用一個CPU或一條收集線程去完成垃圾收集工作,更重要的是在它進行垃圾收集時,必須暫停其他所有的工作線程(Stop the world),直到它收集結束。

優勢

  簡單而高效(與其他收集器的單線程比),對於限定單個CPU的環境來說,Serial收集器由於沒有線程交互的開銷,專心做垃圾收集自然可以獲得最高的單線程收集效率。

 應用場景

  在用戶的桌面應用場景中,分配給虛擬機管理的內存一般來說不會很大,收集兒十兆甚至一兩百兆的新生代(僅僅是新生代使用的內存,桌面應用基本上不會再大了),停頓時間完全可以控制在幾十毫秒最多一百多毫秒以內,只要不是頻繁發生,這點停頓是可以接受的。所以,Serial 收集器對於運行在Client模式下的虛擬機來說是一個很好的選擇。

 參數控制

  -XX:+UseSerialGC    -- 串行收集器

ParNew收集器

  ParNew收集器其實就是Serial收集器的多線程版本;

 特性

  ParNew收集器其實就是Serial收集器的多線程版本,除了使用多條線程進行垃圾收集之外,其餘行爲包括Serial收集器可用的所有控制參數、收集算法、Stop The World、對象分配規則、回收策略等都與Serial收集器完全一樣,在實現上,這兩種收集器也共用了相當多的代碼。

 優勢

  隨着可以使用的CPU的數量的增加,它對於GC時系統資源的有效利用還是很有好處的。

  它默認開啓的收集線程數與CPU的數量相同,在CPU非常多(譬如32個,現在CPU動輒就4核加超線程,服務器超過32個邏輯CPU的情況越來越多了)的環境下,可以使用-XX: ParaIlelGCThreads參數來限制垃圾收集的線程數。

 劣勢

  ParNew收集器在單CPU的環境中絕對不會有比Serial收集器更好的效果,甚至由於存在線程交互的開銷,該收集器在通過超線程技術實現的兩個CPU的環境中都不能百分之百地保證可以超越Serial收集器。

 應用場景

  ParNew收集器是許多運行在Server模式下的虛擬機中首選的新生代收集器。

 參數控制

  -XX:+UseParNewGC            -- ParNew 年輕代收集器

  -XX:ParaIlelGCThreads        -- 限制垃圾收集的線程數

 備註

  除了Serial收集器外,目前只有它能與CMS收集器配合工作。

Parallel Scavenge收集器 [ˈpærəlel]  [ˈskævɪndʒ]

 特性

  相比其他收集器,Parallel收集器更關注系統的吞吐量

  吞吐量 = 運行用戶代碼時間 / (運行用戶代碼時間 + 垃圾收集時間)

 優勢

  GC自適應的調節策略,當使用-XX:+UseAdaptiveSizePolicy參數後,就不需要手工指定新生代的大小、Eden與Survivor區的比例、晉升老年代對象年齡等細節參數了,只需要把基本的內存數據設置好(如-Xmx),然後使用MaxGCPauseMillis參數或GCTimeRatio參數給VM設立一個優化目標,虛擬機會根據當前系統的運行情況收集性能監控信息,動態調整這些參數以提供最合適的停頓時間或者最大的吞吐量,這種調節方式稱爲GC自適應的調節策略(GC Ergonomics)。

 應用場景

  如果對於收集器運作原理不太瞭解手工優化存在困難的時候,使用Parallel Scavenge收集器配合自適應調節策略,把內存管理的調優任務交給虛擬機去完成將是一個很不錯的選擇。

 參數控制

  -XX:+UseParallelGC                -- 使用Parallel收集器 + 老年代串行

  -XX:+UseAdaptiveSizePolicy   -- GC自適應的調節策略

  收集器提供了兩個參數用於精確控制吞吐量:

  -XX:MaxGCPauseMillis     -- 最大垃圾收集停頓時間,是一個大於0的毫秒數,收集器將盡力保證內存回收花費的時間不超過設定值,但是GC停頓時間縮短是以犧牲吞吐量和新生代空間來換取的;

  -XX:GCTimeRatio              -- 直接設置吞吐量大小();默認值爲99,就是允許最大1%的垃圾收集時間。

Serial Old收集器

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

 

 特性

  老年代,單線程收集器,使用“標記-整理”算法

 優勢

 應用場景

  ①Client模式

  Serial Old收集器的主要意義也是在於給Client模式下的虛擬機使用。

  ②Server模式

  如果在Server模式下,那麼它主要還有兩大用途:一種用途是在JDK 1.5以及之前的版本中與Parallel Scavenge收集器搭配使用,另一種用途就是作爲CMS收集器的後備預案,在併發收集發生Concurrent Mode Failure時使用。

 參數控制

 備註

Parallel Old收集器

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

 

 特性

  老年代多線程收集器,使用“標記—整理”算法,該收集器在JDK1.6中才開始提供。

 優勢

 應用場景

  在注重吞吐量以及CPU資源敏感的場合,都可以優先考慮Parallel Scavenge + Parallel Old收集器兩者的組合。

 參數控制

   -XX:+UseParallelOldGC     -- 使用Parallel收集器+ 老年代並行

CMS收集器

  CMS(Concurrent Mark Sweep)收集器,是一種以獲取最短回收停頓時間爲目標的收集器。

 

 特性

  從名字(包含“Mark Sweep”)上就可以看出CMS收集器是基於“標記-清除”算法實現的,它的運作過程相對於前面幾種收集器來說要更復雜一些,整個過程分爲4個步驟,包括: 

  ①初始標記(CMS initial mark):僅僅只是標記一下GC Roots能直接關聯到的對象,速度很快,需要“Stop The World”

  ②併發標記(CMS concurrent mark):通過根搜索算法(GC Roots Tracing)判斷對象是否仍在使用中。

  ③重新標記(CMS remark):爲了修正併發標記期間因用戶程序繼續運作而導致標記產生變動的那一部分對象的標記記錄,這個階段的停頓時間一般會比初始標記階段稍長一些,但遠比並發標記的時間短,仍然需要“Stop The World”

  ④併發清除(CMS concurrent sweep):清除標記對象。

 優勢

  併發收集,低停頓。

 缺點

  ①CMS收集器對CPU資源非常敏感:其實,面向併發設計的程序都對CPU資源比較敏感。在併發階段,它雖然不會導致用戶線程停頓,但是會因爲佔用了一部分線程(或者說CPU資源)而導致應用程序變慢,總吞吐量會降低。

  ②CMS收集器無法處理浮動垃圾:由於CMS併發清理階段用戶線程還在運行着,伴隨程序運行自然就還會有新的垃圾不斷產生,這一部分垃圾出現在標記過程之後,CMS無法在當次收集中處理掉它們,只好留待下一次GC時再清理掉,這一部分垃圾就稱爲“浮動垃圾”。

  ③CMS收集器會產生大量空間碎片:CMS是一款基於“標記—清除”算法實現的收集器,這意味着收集結束時會有大量空間碎片產生。空間碎片過多時,將會給大對象分配帶來很大麻煩,往往會出現老年代還有很大空間剩餘,但是無法找到足夠大的連續空間來分配當前對象,不得不提前觸發一次Full GC。爲了解決這個問題,CMS收集器提供了兩個開關參數-XX:+ UseCMSCompactAtFullCollection && -XX:+CMSFullGCsBeforeCompaction;

 應用場景

  目前很大一部分的Java應用集中在互聯網站或者B/S系統的服務端上,這類應用尤其重視服務的響應速度,希望系統停頓時間最短,以給用戶帶來較好的體驗。CMS收集器就非常符合這類應用的需求。

 參數控制

  -XX:+UseConcMarkSweepGC                      -- 使用CMS收集器

  -XX:+ UseCMSCompactAtFullCollection     -- 用於在“享受”完FullGC之後,免費附贈一次碎片整理過程,由於內存整理的過程是無法併發的,所以時間會比較長;

  -XX:+CMSFullGCsBeforeCompaction          -- 用於執行完N次不壓縮FullGC之後,跟着來一次帶壓縮的;

  -XX:ParallelCMSThreads(後期又稱爲ConcGCThreads)     -- 定義併發CMS過程運行時的線程數。如果該標誌未設置,JVM會根據並行收集器中的-XX:ParallelGCThreads參數的值來計算出默認的並行CMS線程數。ParallelCMSThreads = (ParallelGCThreads + 3)/4,ParallelGCThreads默認會被設置爲CPU的數量;

G1收集器

  G1收集器是垃圾收集器理論進一步發展的產物,HotSpot開發團隊賦予它的使命是未來可以替換掉JDK 1.5中發佈的CMS收集器。

  G1收集器打破了年輕代與老年代的物理界限

 

  取而代之的是,G1算法將堆劃分爲若干個區域(Region),它仍然屬於分代收集器。如下圖所示,堆被分爲 EdenSurvivorold generation區。此外,還有第四種類型的對象被稱爲巨無霸區域(Humongous regions),用來保存比標準塊(standard region)大50%及以上的對象,它們存儲在一組連續的區中,最後一個類型是堆內存中的未使用區(unused areas).

 

特性

  基於“標記一整理”算法,可獨立完成分代收集,打破年輕代與老年代的物理界限,可預測停頓

 優勢

  ①不會產生內存碎片基於“標記一整理”算法,也就是說它不會產生空間碎片,這對於長時間運行的應用系統來說非常重要。同時也不會因爲分配大對象時無法找到連續內存空間而提前觸發下一次GC。

  ②可預測停頓它可以非常精確地控制停頓,既能讓使用者明確指定在一個長度爲M毫秒的時間片段內,消耗在垃圾收集上花費的時間不得超過N毫秒,這幾乎已經是實時Java (RTSJ)的垃圾收集器的特徵。

 應用場景

  可以像CMS收集器一樣,GC操作與應用的線程一起併發執行;

  需要可預測的GC暫停耗時;

 參數控制

  -XX:+UseG1GC                 -- 啓用G1收集器

  -XX:MaxGCPauseMillis     -- 設置GC的最大暫停時間(單位:毫秒)

  -XX:InitiatingHeapOccupancyPercent     -- 啓動併發GC時的堆內存佔比,基於整個堆的使用率,而不只是某一代內存的使用比例;默認值爲 45;值爲 0 則表示“一直執行GC循環“;

 備註

  ①不要設置年輕代的大小(Young Generation Size),假若通過 -Xmn 顯式地指定了年輕代的大小, 則會干擾到 G1收集器的默認行爲;

  ②設置 MaxGCPauseMillis 時不應該使用平均響應時間作爲指標,而應該考慮使用目標時間的90%或者更大作爲響應時間指標.。也就是說90%的用戶請求響應時間不會超過預設的目標值,不過該暫停時間只是一個目標,並不能保證總是得到滿足。


參考:

《深入理解Java虛擬機:JVM高級特性與最佳實踐_周志明》

java垃圾回收器:http://www.jianshu.com/p/50d5c88b272d

http://www.cnblogs.com/ityouknow/p/5614961.html

❤G1收集器:http://blog.jobbole.com/109170/

http://blog.csdn.net/renfufei/article/details/41897113

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章