上一篇博客咱們介紹了Java虛擬機垃圾回收,介紹了幾種經常使用的垃圾回收算法,包括標記-清除,標記整理,複製等,這些算法咱們能夠看作是內存回收的理論方法,那麼在Java虛擬機中,由誰來具體實現這些方法呢?html
沒錯,就是本篇博客介紹的內容——垃圾收集器。java
事實上Java虛擬機規範對垃圾收集器應該如何實現,並無任何的規定,因此不一樣的廠商、不一樣版本的虛擬機所提供的垃圾收集器都會有所不一樣,而且通常都會提供參數供用戶根據本身的應用特色和要求組合出各個年代所使用的收集器。算法
下圖是基於 Sun HotSpot 虛擬機1.6版 Update 22的虛擬機種類:多線程
由上圖咱們能夠總結出幾個結論:併發
①、新生代垃圾收集器:Serial、ParNew、Parallel Scavenge;oracle
老年代垃圾收集器:Serial Old(MSC)、Parallel Old、CMS;ide
整堆垃圾收集器:G1性能
②、垃圾收集器之間的連線表示能夠搭配使用,有以下幾種組合:ui
Serial/Serial Old、Serial/CMS、ParNew/Serial Old、ParNew/CMS、Parallel Scavenge/Serial Old、Parallel Scavenge/Parallel Old、G1;線程
③、串行收集器Serial:Serial、Serial Old
並行收集器 Parallel:Parallel Scavenge、Parallel Old
併發收集器:CMS、G1
ps:對於文章中有一些名詞不理解的,能夠先看本篇博客最後一個小節。
這是一個最基本,歷史最悠久的垃圾收集器,是JDK1.3以前新生代惟一的垃圾收集器。
該收集器有以下特色:
①、做用於新生代
由上圖也可看出,這是一個新生代垃圾收集器,採用的垃圾回收算法是複製算法。
②、單線程
工做時只會使用一個CPU或者一條收集線程去完成工做。
③、進行垃圾收集時,必須暫停全部工做線程
也就是說使用Serial收集器進行垃圾回收時,別的工做線程都暫停,系統這時候會有卡頓現象產生。
④、適用場景
Serial 收集器因爲沒有線程交互的開銷,對於限定單個CPU的環境,能夠得到最高的單線程收集效率。
通常在用戶的桌面場景中,分配給虛擬機管理的內存通常來講不會很大,收集幾十兆或一兩百兆的新生代,定頓時間能夠控制在幾十毫秒,只要不是頻繁發生的,這點停頓是能夠接受的。
因此 Serial 收集器對於運行在 Client 模式下的虛擬機是一種很好的選擇。
這個收集器其實就是Serial收集器的多線程版本。
也就是說其特色除了多線程,其他和Serial收集器同樣,事實上,這兩個收集器實現上也共用了不少代碼。
①、做用於新生代
一個新生代垃圾收集器,採用的垃圾回收算法是複製算法。
②、多線程
彌補了Serial收集器單線程的缺陷。
③、適用場景
因爲其多線程的特性,是大多數運行在 Server 模式下的虛擬機首選新生代垃圾收集器。
另外須要說明的是,可以與下面將要介紹的劃時代垃圾收集器CMS(Concurrent Mark Sweep)配合使用,也是一個重要緣由。
前面介紹的垃圾收集器關注點是儘量縮小垃圾收集時的用戶線程停頓時間。而 Parallel Scanvenge 收集器是爲了達到一個可控制的吞吐量。
吞吐量 = 運行用戶代碼的時間 / (運行用戶代碼的時間+垃圾收集時間)
能夠用下面兩個參數進行精確控制:
-XX:MaxGCPauseMills 設置最大垃圾收集停頓時間
-XX:GCTimeRatio 設置吞吐量大小
①、做用於新生代
一個新生代垃圾收集器,採用的垃圾回收算法是複製算法。
②、多線程
並行的多線程垃圾收集器。
③、吞吐量
這個收集器能夠精確控制吞吐量。
④、適用場景
設置垃圾收集停頓時間短適合須要與用戶快速交互的程序;
而設置高吞吐量能夠最高效的利用CPU效率,儘快的完成程序的運算任務,主要適合在後臺運算而不須要太多交互的任務。
Serial Old 收集器是 Serial 收集器的老年代版本,特色以下:
①、做用於老年代
②、單線程
③、使用標記-整理算法
④、進行垃圾收集時,必須暫停全部工做線程
Parallel Old 是 Parallel Scavenge收集器的老年代版本,使用多線程和「標記-整理」算法。
①、做用於老年代
②、多線程
③、使用標記-整理算法
除了具備以上幾個特色,比較關鍵的是能和新生代收集器 Parallel Scavenge 配置使用,得到吞吐量最大化的效果。
CMS,全稱爲 Concurrent Mark Sweep ,顧名思義併發的,採用標記-清除算法。另外也將這個收集器稱爲併發低延遲收集器(Concurrent Low Pause Collector)
這是一款跨時代的垃圾收集器,真正作到了垃圾收集線程與用戶線程(基本上)同時工做。和 Serial 收集器的 Stop The World(媽媽打掃房間的時候,你不能再將垃圾丟到地上) 相比,真正作到了媽媽一邊打掃房間,你一邊丟垃圾。
①、做用於老年代
②、多線程
③、使用標記-清除算法
整個算法過程分爲以下 4 步:
1、初始標記(CMS initial mark):只是僅僅標記GC Root 可以直接關聯的對象,速度很快,可是須要「Stop The World」
2、併發標記(CMS concurrent mark):進行GC Root Tracing的過程,簡單來講就是遍歷Initial Marking階段標記出來的存活對象,而後繼續遞歸標記這些對象可達的對象。
3、從新標記(CMS Remark):修正併發標記期間,因用戶程序繼續運行而致使標記產生變更的那一部分對象的標記記錄,須要「Stop The World」。這個時間通常比初始標記長,可是遠比並發標記時間短。
4、併發清除(CMS concurrent sweep):對上一步標記的對象進行清除操做。
因爲整個過程最耗時的操做是第二(併發標記)、四步(併發清除),而這兩步垃圾收集器線程是能夠和用戶線程一塊兒工做的。因此總體來講,CMS垃圾收集和用戶線程是一塊兒併發的執行的。
缺點:
①、對CPU資源敏感
由於在併發階段,會佔用一部分CPU資源,從而致使應用程序變慢,總吞吐量會下降。
②、產生浮動垃圾
因爲CMS併發清理階段用戶線程還在工做,這個時候產生的垃圾,CMS沒法在本次收集中處理掉它們,只能留在下一次GC時再將其處理掉,這部分垃圾稱爲「浮動垃圾」。
③、產生內存垃圾碎片
由於採用的算法是標記-清除,很明顯,會有空間碎片產生。
這是當前收集器技術發展的最前沿的成果。能夠實如今基本不犧牲吞吐量的前提下完成低停頓的內存回收。
這是由於它並不像前面介紹的全部垃圾收集器是區分新生代,老年代的,它做用於全區域。將整個Java堆劃分爲多個大小固定的獨立區域(Regin),而且跟蹤這些區域的垃圾堆積面積,在後臺維護一個優先級列表,每次根據容許的收集時間,優先回收垃圾最多的區域,這樣保證了G1收集器在有限的時間內能夠得到最高的收集效率。
它與前面講的 CMS 垃圾收集器相比,有兩個顯著的改進:
①、採用 標記-整理 的回收算法
這樣不會產生空間碎片
②、能夠精確的控制停頓時間
能讓使用者明確指定一個長度爲M毫秒的時間片內,消耗在垃圾回收上的時間不超過 N 毫秒。
③、做用於整個Java堆
G1收集器不區分年輕代和老年代,是整堆垃圾收集器。
詳細文檔能夠查看官方介紹,以下
https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/collectors.html
這裏咱們翻譯一下結論:
除非應用程序有至關嚴格的暫停時間要求,不然就讓JVM本身選擇垃圾收集器。而且能夠適當優先調整堆的大小來提升性能。若是還不知足要求,則如下面四點做爲指導:
1. 若是應用程序內存小於100M,那麼使用選項選擇串行收集器-XX:+UseSerialGC
。
2. 若是應用程序將在單核處理器上運行,而且沒有停頓時間的要求,選擇串行-XX:+UseSerialGC
或者 JVM 本身選
3. 若是容許停頓時間超過1秒,選擇並行或 JVM 本身選
4. 若是響應時間比總吞吐量更重要,而且垃圾收集暫停必須保持短於大約1秒,則使用-XX:+UseConcMarkSweepGC
或選擇併發收集器-XX:+UseG1GC
。
①、並行
指多條垃圾收集線程並行工做,但此時用戶線程仍然處於等待狀態。
適合科學計算、後臺處理等弱交互場景。
②、併發
指用戶線程與垃圾收集器線程同時執行(但不必定是並行的,可能會交替執行),用戶線程繼續執行,而垃圾收集線程運行在另外一塊CPU上。
適合對響應快速的場景,好比Web。
③、停頓時間
垃圾收集器作垃圾回收中斷應用執行的時間。
④、吞吐量
吞吐量 = 運行用戶代碼的時間 / (運行用戶代碼的時間+垃圾收集時間)