G1 垃圾收集器架構和如何作到可預測的停頓(阿里)

CMS垃圾回收機制 參考:圖解 CMS 垃圾回收機制原理,-阿里面試題html

CMS與G1的區別 參考:CMS收集器和G1收集器優缺點java

寫這篇文章是基於阿里面試官的一個問題:衆所週期,G1跟其餘的垃圾回收算法差異很大,你瞭解G1的垃圾回收架構嗎?爲何G1能夠作到回收時間用戶能夠設定?git

G1垃圾回收器實際上是JDK7的特性,在目前JDK10都已經發布的狀況下,已經不是什麼新特性了,而我到它如今才關注它,可見我是有多麼的懶;而我終於關注它了,可見個人懶還算是有救的 :)web

G1實際上是Garbage First的意思,垃圾優先? 不是,是優先處理那些垃圾多的內存塊的意思。在大的理念上,它仍是遵循JVM的內存分代假設(其實叫假設不許確,這是從實際Java應用的內存使用觀察獲得的結論):面試

90%的對象熬不過第一次垃圾回收,而老的對象(經歷了好幾回垃圾回收的對象)則有98%的機率會一直活下來。算法

基於這個分代假設,通常的垃圾回收器把內存分紅三類: Eden(E), Suvivor(S)和Old(O), 其中Eden和Survivor都屬於年輕代,Old屬於老年代,新對象始終分配在Eden裏面,熬過一次垃圾回收的對象就被移動到Survisor區了,通過數次垃圾回收以後還活着的對象會被移到Old區。編程

通常GC的內存分佈

這樣分代的好處是,把一個複雜的大問題,分紅兩類不一樣的小問題,針對不一樣的小問題,採用更有針對性的措施(分而治之):瀏覽器

  • 對於年輕代的對象,因爲對象來的快去得快,垃圾收集會比較頻繁,所以執行時間必定要短,效率要高,所以要採用執行時間短,執行時間的長短只取決於對象個數的垃圾回收算法。可是這類回收器每每會比較浪費內存,好比Copying GC,會浪費一半的內存,以空間換取了時間。
  • 對於老年代的對象,因爲自己對象的個數很少,垃圾收集的次數很少,所以能夠採用對內存使用比較高效的算法。

跟其它垃圾回收器不同的是:G1雖然也把內存分紅了這三大類,可是在G1裏面這三大類不是涇渭分明的三大塊內存,G1把內存劃分紅不少小塊, 每一個小塊會被標記爲E/S/O中的一個,能夠前面一個是Eden後面一個就變成Survivor了。安全

 
G1的內存分佈

這麼作給G1帶來了很大的好處,因爲把三塊內存變成了幾百塊內存,內存塊的粒度變小了,從而能夠垃圾回收工做更完全的並行化。性能優化

G1的並行收集作得特別好,咱們第一次聽到並行收集應該是CMS(Concurrent Mark & Sweep)垃圾回收算法, 可是CMS的並行收集也只是在收集老年代可以起效,而在回收年輕代的時候CMS是要暫停整個應用的(Stop-the-world)。而G1整個收集全程幾乎都是並行的,它回收的大體過程是這樣的:

  • 在垃圾回收的最開始有一個短暫的時間段(Inital Mark)會中止應用(stop-the-world)
  • 而後應用繼續運行,同時G1開始Concurrent Mark
  • 再次中止應用,來一個Final Mark (stop-the-world)
  • 最後根據Garbage First的原則,選擇一些內存塊進行回收。(stop-the-world)

因爲它高度的並行化,所以它在應用中止時間(Stop-the-world)這個指標上比其它的GC算法都要好。

G1的另外一個顯著特色他可以讓用戶設置應用的暫停時間,爲何G1能作到這一點呢?也許你已經注意到了,G1回收的第4步,它是「選擇一些內存塊」,而不是整代內存來回收,這是G1跟其它GC很是不一樣的一點,其它GC每次回收都會回收整個Generation的內存(Eden, Old), 而回收內存所需的時間就取決於內存的大小,以及實際垃圾的多少,因此垃圾回收時間是不可控的;而G1每次並不會回收整代內存,到底回收多少內存就看用戶配置的暫停時間,配置的時間短就少回收點,配置的時間長就多回收點,伸縮自如。 (阿里面試)

因爲內存被分紅了不少小塊,又帶來了另外好處,因爲內存塊比較小,進行內存壓縮整理的代價都比較小,相比其它GC算法,能夠有效的規避內存碎片的問題。

說了G1的這麼多好處,也該說說G1的壞處了,若是應用的內存很是吃緊,對內存進行部分回收根本不夠,始終要進行整個Heap的回收,那麼G1要作的工做量就一點也不會比其它垃圾回收器少,並且由於自己算法複雜了一點,可能比其它回收器還要差。所以G1比較適合內存稍大一點的應用(通常來講至少4G以上),小內存的應用仍是用傳統的垃圾回收器好比CMS比較合適。

總結

G1經過在垃圾回收領域應用並行化的策略,把幾塊大內存塊的回收問題,變成了幾百塊小內存的回收問題,使得回收算法能夠高度並行化,同時也由於分紅不少小塊,使得垃圾回收的單位變成了小塊內存,而不是整代內存,使得用戶可能對回收時間進行配置,垃圾回收變得能夠預期了。

分而治之、化整爲零這些樸素的架構思想每每是不少牛叉技術產品背後的思想根源啊。

 

參考:G1垃圾回收器

 

總結這篇文章和其餘的資料,G1能夠基本穩定在0.5s到1s左右的延遲,可是並不能保證更低的好比毫秒級(金融場景,因此說涉及到錢的,對技術要求真高),號稱zing能夠(可是通常作到低延時,在其餘方面確定有所損耗,好比吞吐),可是沒有實際去研究過這種。另外,G1也可能和CMS同樣出現Full GC,若是區域不夠提高的話,因此它通常用於需求更大的堆中。但G1最顯著於CMS的,在於它對空間作了整理,這樣減小了空間的碎片化。CMS的空間碎片話相較於G1要嚴重不少,試想下它的Mark-Sweep以後的空間,有不少小碎片,可是都比要分配的小,而後觸發一次Full GC,簡直了。再說點雜的,G1的思想,感受有點像Java CocurrentHashMap,也是將一個大的分紅若干個Region,而後再處理

G1 垃圾收集器入門

概覽


目的

這個教程覆蓋瞭如何使用G1垃圾收集器和它是怎樣被Hotspot JVM使用的,你會學到G1收集器內部是如何工做的,使用G1時的一些關鍵命令行開關和記錄它的操做的一些選項。

完成耗時

大約1小時

介紹

這個OBE(Oracle By Example)覆蓋了Java裏的Java虛擬機G1垃圾回收的基本概念,在OBE的第一部分, 
在介紹垃圾收集器和性能時會附帶提供JVM的概覽。下一部分回顧一下Hotspot JVMCMS收集器如何工做。而後,一步一步來指導使用Hotspot JVMG1垃圾回收,跟着,會用一段來說G1垃圾回收器的可用的命令行選項。最後,你會學到G1回收器的日誌選項。

硬件、軟件需求

下面是一個硬件軟件需求列表:

  • 一臺運行Windows XP或者更高版本的PC,Mac OS X或者Linux。注意已在Windows 7系統親測,尚未在全部平臺測試。然而,應該會在OS X或Linux上正常運行。多處理器核心的機器更好。
  • Java 7 Update 9或更高版本
  • 最新Java 7 演示和示例壓縮文件

先決條件

開啓教程以前,你應該:

  • 若是你沒有這樣作,下載安裝最新版本的JDK,Java 7 JDK Downloads
  • 從一樣的地址下載安裝演示示例壓縮文件,解壓文件到一個目錄中,好比:C:/javademos

Java技術和虛擬機


Java概覽

Java是Sun微系統公司在1995年首次發佈的一個編程語言和計算平臺。它是支撐Java程序包括工具、遊戲、商業應用的底層技術。在全球Java運行在超過8億5千萬臺我的電腦上,數以十億計的設備上,包括移動、電視設備。Java由一些關鍵組件組成,做爲一個總體,建立Java平臺。

Java運行時

當你下載Java,你得到Java運行環境(JRE,Java Runtime Environment)。 JRE由Java虛擬機(JVM)、Java平臺核心類和支撐Java平臺的庫組成。要在你電腦上運行Java應用,全部的這些都是必須的。有了Java 7, Java應用從操做系統方面來講是做爲桌面應用運行的,做爲一個桌面應用可是從網絡上安裝須要使用Java Web Start,或者在瀏覽器裏做爲一個Web嵌入式的應用(使用JavaFX

Java編程語言

Java是一個面向對象的編程語言,包含如下特性:

  • 平臺獨立 - Java應用被編譯成存儲在類文件中的字節碼,在JVM裏被加載。一旦應用在JVM裏運行,它們能夠運行在許多不一樣的操做系統和設備上。
  • 面向對象 - Java是一個面向對象的語言,借鑑了C和C++的諸多特性,在它們之上改進。
  • 自動垃圾收集 - Java自動分配和釋放內存,因此程序沒必要揹負這個任務。
  • 豐富的標準庫 - Java包括大量預先作好的對象,它用被用在執行好比輸入/輸出、網絡、數據操做這樣的任務上。

Java開發工具箱

Java開發工具箱(JDK,Java Development Kit)是一個開發Java應用的工具集。有了JDK,你能夠編譯用Java語言編寫的應用程序,在JVM裏面運行它們。另外,JDK提供工具打包和發佈你的應用。

JDK和JRE分享一樣的Java應用編程接口(Java API,Java Application Programming Interfaces)。Java API是已經打包的庫的集合,開發者用來建立Java應用。Java API經過提供工具完成不少一般的編程任務包括字符串操做、時間日期處理、網絡和實現數據結構(好比:列表、映射、棧和隊列)使開發更加容易。

Java虛擬機

Java虛擬機(JVM,Java Virtual Machine)是一個抽象的計算機器,Java虛擬機是一個程序,對在它裏面運行的編寫的程序來講, 看起來像一個機器。這樣,Java程序就會用相同的接口和庫來編寫。每個針對特定操做系統的JVM實現,把Java程序指令翻譯成運行在本地操做系統的指令和命令。這樣,Java程序實同了平臺獨立。

Sun微系統公司完成了第一個Java虛擬機的原型實現,仿真Java虛擬機指令集設置進一個被相似當時的我的數碼助手(PDA,Personal Digital Assistant)手持設備的託管的軟件裏。Oracle如今實現仿真Java虛擬機在移動、桌面和服務器設備上,可是Java虛擬機沒有承擔任何具體的技術實現,管理硬件或者管理操做系統。It is not inherently interpreted, but can just as well be implemented by compiling its instruction set to that of a silicon CPU. It may also be implemented in microcode or directly in silicon斜體這裏翻譯不清楚,請大神指點。好像涉及解釋執行、指令集、微碼等

Java虛擬機對Java編程語言一律不知,只知道一個特定的二進制格式,就是類文件格式,一個類文件包含Java虛擬機指令(或者叫字節碼)和一個符號表,和一些輔助信息。

爲了達到安全的目的,Java虛擬機在類文件代碼上利用強大的語法和結構化的約束條件。然而任何函數性語言能夠依照一個可用的被Java虛擬機託管類文件來表達。被通用的、機器平臺獨立性吸引,其它語言的實現者能夠把Java虛擬機視爲其它語言的遞送載體,(1)The Java Virtual Machine

探索Java虛擬機架構

Hotspot架構

Hotspot虛擬機擁有一個架構,它支持強大特性和能力的基礎平臺,支持實現高性能和強大的可伸縮性的能力。舉個例子,Hotspot虛擬機JIT編譯器生成動態的優化,換句話說,它們在Java應用執行期作出優化,爲底層系統架構生成高性能的本地機器指令。另外,通過它的運行時環境和多線程垃圾回收成熟的進化和連續的設計, Hotspot虛擬機在高可用計算系統上產出了高伸縮性。 
hotspot jvm architecture
Java虛擬機的主要組件,包括類加載器、運行時數據區和執行引擎

Hotspot關鍵組件

Java虛擬機有關性能的關鍵組件已經在下面的圖片上高亮顯示了。 
key hotspot jvm components
Java虛擬機有三個組件關注着何時進行性能優化,堆空間是你的對象所存儲的地方,這個區域 
被啓動時選擇的垃圾回收器管理,大部分調優選項與調整堆大小和根據你的狀況選擇最適當的垃圾收集器相關。即時編譯器對性能也有很大的影響,可是使用新版本的Java虛擬機時不多須要調整。

性能基礎

典型的,當調優一個Java應用時,把焦點放在兩個主要的目標上:響應能力或者吞吐量。隨着教程的進行咱們會再從新說起這些概念。

響應能力

響應能力指的是一個應用迴應一個請求數據的速度有多快。示例包括:

  • 桌面UI響應事件的速度
  • 網站返回網頁的速度
  • 數據查詢返回的速度 
    對關注響應能力的應用來講,長暫停時間是不可接受的,重點是在短的時間週期內能作出響應。

吞吐量

吞吐量關注在特定的時間週期內一個應用的工做量的最大值。舉例如何衡量吞吐量,包括:

  • 給定時間內完成事務的數量
  • 一小時內批處理程序完成的工做數量
  • 一小時內數據查詢完成的數量 
    對關注吞吐量的應用來講長暫停時間是能夠接受的。因爲高吞吐量的應用關注的基準在更長週期時間上,因此快速響應時間不在考慮以內。

G1垃圾回收器


G1垃圾回收器

Garbage-First(G1,垃圾優先)收集器是服務類型的收集器,目標是多處理器機器、大內存機器。它高度符合垃圾收集暫停時間的目標,同時實現高吞吐量。Oracle JDK 7 update 4 以及更新發布版徹底支持G1垃圾收集器。G1垃圾回集器爲如下應用設計:

  • 相似CMS收集器,能夠和應用線程同時併發的執行
  • 壓縮空閒空間時沒有GC引發的暫停時間
  • 須要更可預言的GC暫停時間
  • 不想犧牲大量的吞吐量性能
  • 不須要特別大的Java堆

G1垃圾收集器計劃長期替換併發標記清除收集器(CMS,Concurrent Mark-Sweep Collector)。G1和CMS比較,有一些不一樣點讓G1成爲一個更好的解決方案。一個不一樣點是G1是一個壓縮收集器。G1收集器充分地壓縮空間以徹底避免爲分配空間使用細粒度的空閒列表,而不是依賴於區塊。這至關簡化了收集器的部件,和儘可能消除可能的碎片問題。同時,G1收集器相比CMS收集器而方言,提供更可預言的垃圾收集暫停時間,容許用戶指定想要暫停時間指標。

G1收集器操做概覽

舊的垃圾收集器(串行的:serial,並行的:parallel,併發標記清除:CMS)都把堆結構化爲三個部分:年輕代、年老代和固定大小的永久代。 
Hotspot Heap Structure
因此內存對象最終都在這三個區域裏。 
G1收集器應用了一個不一樣的方法。 
G1 Heap Allocation
堆空間被分割成一些相同大小的堆區域,每個都是連續範圍的虛擬內存。特定的區域集合像舊的收集器同樣被指派爲相同的角色(伊甸:eden、倖存:survivor、年老:old),可是它們沒有一個固定大小。這在內存使用上提供了更強大的靈活性。

當執行垃圾收集時,G1收集器以與CMS收集器相似的方式操做。G1收集器執行一個全局的併發標記階段來決定堆中的對象的活躍度。以後標記階段就完成了。G1收集器知道哪一個區域基本上是空的。它首先會收集那些產出大量空閒空間的區域。這就是爲何這個垃圾收集的方法叫作垃圾優先的緣由。就像名稱顯示的那樣,G1收集器集中它的收集和壓縮活動在堆裏的那些可徹底被回收的區域,那就是垃圾。G1收集器使用一個暫停預言的模式去達到一個用戶定義的暫停時間指標,基於用戶指定的暫停時間指標去選擇收集區域的數量。

被G1收集器鑑定爲能夠回收的區域就是垃圾,使用抽空的方式收集。G1收集器從堆空間的一個或多個區域裏複製對象到堆空間的一個單獨的區域內,這個過程當中同時壓縮和釋放內存。這個抽空過程在多處理上以並行的方式運行,以減少暫停時間和增長吞吐量。所以,每一次垃圾收集G1收集器接二連三地去減小碎片,在用戶指定的暫停時間內工做。這超越了以往方法的能力。併發標記-清除(CMS,Concurrent Mark Sweep)垃圾收集器不作壓縮操做。並行年老代(ParallelOld)垃圾收集只進行整個堆的壓縮,會致使至關大的暫停時間。

注意: G1收集器不是實時的收集器很是重要。它在很大程度上符合用戶設定的暫停時間指標可是並不絕對符合。基於前面垃圾收集的數據來看,G1收集器會估算在用戶指定的時間指標能收集多少區域。所以,收集器有一個合理的精確的收集這些區域的代價模型,它使用這個模型決定在用戶指定的暫停時間內收集哪些、多少個區域。

注意: G1收集器同時有併發(和應用線程一塊兒運行,好比,提煉、標記、清理)和並行(多線程,好比,stop the world)兩個階段。全量垃圾回收仍然是單線程的,可是若是調優的適當你的應用應該會避免全量垃圾回收。

G1回收器足跡

若是你從ParallelOldGc或者CMS收集器遷移到G1收集器,你頗有可能會看到一個大的Java虛擬機進程大小,這和審計」數據結構好比已記憶集合(Remembered Sets)和收集集合(Collection Sets)有很大關係「。

Remembered Sets或者RSets把對象引用推動一個給定的區域。在堆空間中每個區有一個RSet。RSet容許一個區域並行的、獨立的收集。RSet整體的足跡影響小於5%。

Collection Sets或者CSets,是在垃圾回收過程當中會被回收的區域集合。在RSet中的全部活躍對象在垃圾回收過程當中會被抽空(複製/移動)。集合包含的區域能夠是eden、survivor或者年老代。CSets在Java虛擬機大小的影響小於1%。

建議使用G1收集器的場景

G1收集器首要關注的是爲用戶運行着須要大堆空間、限制的垃圾回收延遲的應用提供一個解決方案。這意味着堆大小爲6GB左右或者更大,穩定的、可預言的暫停時間小於0.5秒。

若是應用有如下一個或多個特色,當下運行着CMS或ParallelOldGC垃圾收集器的應用把收集器切換到G1收集器的話,會從中受益的:

  • Full GC持續時間太長或者太頻繁
  • 對象分配比率或者提高有顯著的變化
  • 不指望的長時間垃圾收集或者壓縮暫停(大於0.5到1秒)

注意:若是你在使用CMS或者ParallenOldGC收集器,你的應用未曾經歷過長時間的垃圾收集暫停,保持使用你當前的收集器比較好。在使用最新的JDK的狀況下,改變到G1收集器不是一個必要的事情。

回顧CMS垃圾回收


回顧分代垃圾回收和CMS

併發標記清除(CMS)收集器(也叫併發低延遲收集器)回收年老代垃圾。它經過和應用線程併發的執行大部分垃圾收集工做的方式來嘗試最小化垃圾回收引發的暫停。正常狀況下併發低延遲收集器不會複製或者壓縮活躍對象。一次垃圾收集的完成沒必要移動活躍對象。若是內存碎片成爲一個問題,分配更大的堆空間。

注意: CMS收集器在年輕代上使用和並行收集器相同的算法。

CMS收集階段

CMS收集器在堆的年老代空間上執行如下階段:

階段 描述
(1)初始標記(Stop the World事件) 年老代裏的對象被標記爲可達的包括那些可能從年輕代可達的對象。此期間暫停時間相對minor gc的暫停時間是比較 短的
(2)併發標記 當Java應用線程運行時,併發的遍歷年老代對象圖可達的對象。從標記的對象和根上可達到標記對象開始掃描。設值方法在併發的二、三、5階段期間執行,在這些階段(包括晉升的對象)被分配進CMS代全部對象都會馬上被標記爲活躍對象。
(3)從新標記(Stop the World事件) 尋找那些在併發標記階段丟失的,在併發收集器完成以後跟蹤對象以後由Java應用線程的更新的對象。
(4)(併發清除) 收集在標記階段被鑑定爲不可達的對象。收集死亡對象會增長空閒列表的空間,方便以後的對象分配。聚合死亡對象能夠會在此點發生。注意活躍對象是不會被移動。
(5)(從新設置) 清理數據結構爲下一次併發收集作準備

回顧垃圾收集步驟

接下來,讓咱們一步一步的回顧下CMS收集器的操做步驟

1. CMS收集器堆結構

堆空間被分割爲三塊空間。 
CMS Heap Structure
年輕代分割成一個Eden區和兩個Survivor區。年老代一個連續的空間。就地完成對象收集。除非有FullGC不然不會壓縮。

2.CMS年輕代垃圾收集如何工做

年輕代被標爲淺綠色,年老代被標記爲藍色。若是你的應用已經運行了一段時間,CMS的堆看起來應該是這個樣子。對象分散在年老代區域裏。 
How Young GC Works
使用CMS,年老代對象就地釋放。它們不會被來回移動。這個空間不會被壓縮除非發生FullGC。

3.年輕代收集

從Eden和Survivor區複製活躍對象到另外一個Survivor區。全部達到他們的年齡閾值的對象會晉升到年老代。 
Young Generation Collection

4.年輕代回收以後

一次年輕代垃圾收集以後,Eden區和其中一個Survivor區被清空。 
After Young GC
最近晉升的對象以深藍色顯示在上圖中,綠色的對象是年輕代倖免的尚未晉升到老年代對象。

5.CMS的年老代收集

發生兩次stop the world事件:初始標記和從新標記。當年老代達到特定的佔用比例時,CMS開始執行。 
Old gen collection in CMS
(1)初始標記是一個短暫暫停的、可達對象被標記的階段。(2)併發標記尋找活躍對象在應用連續執行時。最後,在(3)從新標記階段,尋找在以前併發標記階段中丟失的對象。

6.年老代收集-併發清除

在以前階段沒有被標記的對象會被就地釋放。不進行壓縮操做。 
Old Gen Collection - Concurrent Sweep
注意:未被標記的對象等於死亡對象

7.年老代收集-清除以後

(4)清除階段以後,你能夠看到大量內存被釋放。你還能夠注意到沒有進行壓縮操做。 
Olg Gen Collection - After Sweeping
最後,CMS收集器會走過(5)從新設置階段,等待下一次垃圾收集時機的到來。

按部就班G1垃圾收集器


按部就班G1垃圾收集器

G1收集器在分配堆空間的方法上有些不一樣。下面的圖片一步一步系統的回顧G1收集器。

1.G1堆結構

堆空間是一個被分紅許多固定大小區域的內存塊。 
G1 Heap Structure
Java虛擬機啓動時選定區域大小。Java虛擬機一般會指定2000個左右的大小相等、每一個大小範圍在1到32M的區域。

2.G1堆空間分配

實際上,這些區域被映射成Eden、Survivor、年老代空間的邏輯表述形式。 
G1 Heap Allocation
圖片中的顏色代表了哪一個區域被關聯上什麼角色。活躍對象從一個區域疏散(複製、移動)到另外一個區域。區域被設計爲並行的方式收集,能夠暫停或者不暫停全部的其它用戶線程。

明顯的區域能夠被分配成Eden、Survivor、Old區域。另外,有第四種類型的區域叫作極大區域(Humongous regions)。這些區域被設計成保持標準區域大小的50%或者更大的對象。它們被保存在一個連續的區域集合裏。最後,最後一個類型的區域就是堆空間裏沒有使用的區域。

注意:寫做此文章時,收集極大對象時尚未被優化。所以,你應該避免建立這個大小的對象。

3.G1的年輕代

堆空間被分割成大約2000個區域。最小1M,最大32M,藍色區域保持年老代對象,綠色區域保持年輕代對象。 
Young Generation in G1
注意:區域沒有必要像舊的收集器同樣是保持連續的。

4.G1的年輕代收集

活躍對象會被疏散(複製、移動)到一個或多個survivor區域。若是達到晉升總閾值,對象會晉升到年老代區域。 
A Young GC in G1
這是一個stop the world暫停。爲下一次年輕代垃圾回收計算Eden和Survivor的大小。保留審計信息有助於計算大小。相似目標暫停時間的事情會被考慮在內。

這個方法使重調區域大小變得很容易,按需把它們調大或調小。

5.G1年輕代回收的尾聲

活躍對象被疏散到Survivor或者年老代區域。 
End of Young GC with G1
最近晉升的對象顯示爲深藍色。Survivor區域顯示爲綠色。

關於G1的年輕代回收作如下總結:

  • 堆空間是一塊單獨的內存空間被分割成多個區域。
  • 年輕代內存是由一組非連續的區域組成。這使得須要重調大小變得容易。
  • 年輕代垃圾回收是stop the world事件,全部應用線程都會所以操做暫停。
  • 年輕代垃圾收集使用多線程並行回收。
  • 活躍對象被複制到新的Survivor區或者年老代區域。

G1年老代垃圾回收

相似CMS收集器,G1收集器爲年老代對象被設計成一個低暫停收集器。下面的表描述了在年老代上的G1收集階段。 
G1垃圾收集器在堆上的年老代執行如下階段。注意一些階段是年輕代回收的一部分。

階段 描述
(1)初始標記(stop the world事件) 這是一個stop the world事件,使用G1回收器,揹負着一個常規的年輕代收集。標記那些有引用到年老代的對象的survivor區(根區)
(2)根區掃描 爲到年老代的引用掃描survivor區,這個發生在應用繼續運行時。這個階段在年輕代收集前必須完成
(3)併發標記 遍歷整個堆尋找活躍對象,這個發生在應用運行時,這個階段能夠被年輕代垃圾回收打斷。
(4)從新標記(stop the world事件) 徹底標記堆中的活躍對象,使用一個叫做snapshot-at-the-beginning(SATB)的比CMS收集器的更快的算法
(5)清理(stop the world事件和併發) 在活躍對象上執行審計操做和釋放區域空間(stop the world);淨化已記憶集合(stop the world);重置空間區域和返回它們到空閒列表(併發)
(*)複製(stop the world事件) 這些是stop the world暫停爲了疏散或者複製活躍對象到新的未使用的區域。這個能夠由被記錄爲[GC Pause (young)]的年輕代區域或者被記錄爲[GC Pause (mixed)]年輕代和年老代區域完成

按部就班G1年老代垃圾回收

記住已被定義的階段,讓咱們來看一下G1收集器是如何做用於年老代的。

6.初始標記階段

年輕代垃圾收集肩負着活躍對象初始標記的任務。在日誌文件中被標爲GC pause (young)(inital-mark) 
Initial Marking Phase

7.併發標記階段

若是發現空區域(「X」標示的),在從新標記階段它們會被立刻清除掉。固然,決定活性的審計信息也在此時被計算。 
Concurrent Marking Phase

8.從新標記階段

空的區域被清除和回收掉。全部區域的活性在此時計算。 
Remark Phase

9.複製/清理階段

G1選擇活性最低的區域,這些區域可以以最快的速度回收。而後這些區域會在年輕代垃圾回收過程當中被回收。在日誌中被指示爲[GC pause (mixed)]。因此年輕代和年老代在同一時間被回收。 
Coping/Cleanup Phase

10.複製/清理階段以後

被選擇的區域已經被回收和壓縮到圖中顯示的深藍色區和深綠色區中。 
After Coping/Cleanup Phase

年老代垃圾回收總結

總結下,咱們能夠列出一些關於G1收集器在年老代的上關鍵點。 
併發標記階段

  • 當應用運行時,併發的計算活性信
  • 在疏散暫停期間,活性信息鑑定哪些區被最好的回收
  • 沒有像CMS同樣的清除操做

從新標記階段

  • 使用比在CMS中使用的算法更快的Snapshot-at-the-Beginning(SATB)算法
  • 徹底空的區域會被回收掉

複製/清理階段

  • 年輕代和年老代被同時回收
  • 年老代區域基於它們的活性被選擇

命令行選項最佳實踐


命令行選項最佳實踐

在這部分咱們看一下G1收集器的多樣的命令行選項。

基本命令行

爲了啓用G1收集器,使用:-XX:+UseG1GC 
這個是啓動在已下載的JDK演示和示例裏的Java2Demo程序的示例命令行: 
java -Xmx50m -Xms50m -XX:UserG1GC -XX:MaxGCPauseMillis=200 -jar c:\javademos\demo\jfc\Java2D\Java2demo.jar

關鍵命令行開關

-XX:+UseG1GC - 告訴Java虛擬機使用G1垃圾收集器 
-XX:MaxGCPauseMillis=200 - 爲最大GC暫停時間設置一個指標。這是一個軟目標,Java虛擬機將盡最大努力實現它。所以,暫停時間目標有時候可能不會達到。默認值是200毫秒。 
-XX:InitiatingHeapOccupancyPercent=45 - 觸發併發垃圾收集週期的整個堆的百分比時機。

最佳實踐

使用G1收集器時你應該遵照的一些最佳實踐 
不要設置年輕代大小 
經過-Xmn明確地設置年輕代大小來插手G1收集器的默認行爲。

  • 收集時G1收集器將再也不遵守暫停時間指標。因此本質上,設置年輕代大小將不會啓用暫停時間目標。
  • G1收集器將不能按需擴張、收縮年輕代空間。自從大小被固定以後,大小將再也不會被改變。

響應時間指標

代替使用平均響應時間(ART)作爲指標,來設置XX:MaxGCPauseMillis=,考慮設置值將會符合這個時間的90%或者更高比例。這意味着90%的用戶發出一個請求將不會經歷高於這個目標的時間。記住,暫停時間只是一個目標,不保證老是可以達到。

什麼是疏散失敗?

當Java虛擬機在Survivor和晉升的對象垃圾回收期間,堆空間用光了就會發生晉升失敗。堆空間不能再擴展了由於已經在最大值了,使用-XX:+PrintGCDetails參數時,這種狀況會在GC日誌中經過to-space-overflow指示出來。這個代價很是大。

  • 垃圾收集仍然會繼續運行,空間必須被釋放。
  • 沒有成功複製的對象必須就地被提高。
  • 在CSet裏的任何到區域的RSets的更新都會從新生成
  • 全部這些步驟代價都很是大
如何避免疏散失敗

爲了不疏散失敗,考慮如下選項。 
增大堆大小

  • 增大-XX:G1ReservePercent=n參數值,默認是10
  • G1收集器建立一個假的上限經過嘗試保留儲備內存的自由假如’to-space’被渴望獲得。 
    提早啓動標記週期 
    使用-XX:ConcGCThreads=n選項增大標記線程的數量
G1垃圾收集器開關完整列表

這是一個G1垃圾收集器開關的完整列表,記着去使用上述的最佳實踐。

選項和默認值 描述
-XX:+UseG1GC 使用垃圾優先(G1,Garbage First)收集器
-XX:MaxGCPauseMillis=n 設置垃圾收集暫停時間最大值指標。這是一個軟目標,Java虛擬機將盡最大努力實現它
-XX:InitiatingHeapOccupancyPercent=n 觸發併發垃圾收集週期的整個堆空間的佔用比例。它被垃圾收集使用,用來觸發併發垃圾收集週期,基於整個堆的佔用狀況,不僅是一個代上(好比:G1)。0值 表示’do constant GC cycles’。默認是45
-XX:NewRatio=n 年輕代與年老代的大小比例,默認值是2
-XX:SurvivorRatio=n eden與survivor空間的大小比例,默認值8
-XX:MaxTenuringThreshold=n 最大晉升閾值,默認值15
-XX:ParallerGCThreads=n 設置垃圾收集器並行階段的線程數量。默認值根據Java虛擬機運行的平臺有所變化
-XX:ConcGCThreads=n 併發垃圾收集器使用的線程數量,默認值根據Java虛擬機運行的平臺有所變化
-XX:G1ReservePercent=n 爲了下降晉升失敗機率設置一個假的堆的儲備空間的上限大小,默認值是10
-XX:G1HeapRegionSize=n 使用G1收集器,Java堆被細分紅一致大小的區域。這設置個體的細分的大小。這個參數的默認值由工學意義上的基於堆的大小決定

G1收集器的垃圾收集日誌


G1收集器的垃圾收集日誌

咱們須要涵蓋的最後的主題是使用G1垃圾回收器的日誌記錄信息來分析性能。這部分提供你能夠用來收集打印在日誌裏的數據和信息的開關的快速的概覽。

設置日誌詳情

你能夠設置三種不一樣級別的詳情。 
(1)-verbosegc (和-XX:+PrintGC等效)參數設置fine日誌詳情級別 
sample Output 
[GC pause (G1 Humongous Allocation) (young) (initial-mark) 24M- >21M(64M), 0.2349730 secs] 
[GC pause (G1 Evacuation Pause) (mixed) 66M->21M(236M), 0.1625268 secs]

(2)-XX:PrintGCDetails設置finer詳情級別。使用這個選項會顯示如下信息: 
* 顯示每一個階段的平均、最小、最大時間 
* 根掃描、RSet更新(附帶處理的緩衝信息)、RSet掃描、對象複製、終止(附帶嘗試次數)。 
* 也顯示’other’時間,好比花費在選擇CSet上的時間、引用處理、引用排隊和釋放CSet。 
* 顯示Eden、Survivor和總堆空間佔用。 
Sample Output 
[Ext Root Scanning (ms): Avg: 1.7 Min: 0.0 Max: 3.7 Diff: 3.7] 
[Eden: 818M(818M)->0B(714M) Survivors: 0B->104M Heap: 836M(4096M)->409M(4096M)]

(3)-XX:+UnlockExperimentalVMOptions -XX:G1LogLevel=finest設置finest詳情級別。相似finer可是包括每一個工做者線程的信息。 
Sample Output 
[Ext Root Scanning (ms): 2.1 2.4 2.0 0.0 
Avg: 1.6 Min: 0.0 Max: 2.4 Diff: 2.3] 
[Update RS (ms): 0.4 0.2 0.4 0.0 
Avg: 0.2 Min: 0.0 Max: 0.4 Diff: 0.4] 
[Processed Buffers : 5 1 10 0 
Sum: 16, Avg: 4, Min: 0, Max: 10, Diff: 10]

決定時間顯示格式

有兩個開關能夠決寫在垃圾收集日誌中如何顯示時間。 
(1)-XX:+PrintGCTimeStamps - 顯示自從Java虛擬機啓動以後流逝的時間。 
Sample Output 
1.729: [GC pause (young) 46M->35M(1332M), 0.0310029 secs]

(2)-XX:+PrintGCDateStamps - 爲每一項添加日期時間的前綴。 
Sample Output 
2012-05-02T11:16:32.057+0200: [GC pause (young) 46M->35M(1332M), 0.0317225 secs]

理解G1日誌

爲了理解這個日誌,這部分使用實際的垃圾收集輸出日誌來明確一些術語。下面的示例列出了輸出日誌中的術語和值,你會在日誌中找到它們。 
注意:更多信息請查看Poonam Bajaj’s Blog post on G1 GC logs

G1日誌術語索引

Worker Start 
Parallel Time 
External Root Scanning 
Update Remembered Set 
Scanning Remembered Sets 
Object Copy 
Termination Time 
GC Worker End 
GC Worker Other 
Clear CT 
Other 
CSet 
Ref Proc 
Ref Eng 
Free CSet 
此入門教程源地址上有一些介紹,可是和Poonam Bajaj’s Blog post on G1 GC logs內容幾乎相同,因此詳細信息請看我翻譯的Poonam Bajaj’s Blog post on G1 GC logs

總結


在這個OBE裏,你已經對包含在Java虛擬機裏的G1垃圾收集器有了大概的認識。首先你學習了爲何堆和垃圾收集器是任何Java虛擬機的關鍵部件。而後你回顧了使用CMS收集器和G1收集器的垃圾收集是如何工做的。而後你學習了關於G1收集器的命令行開關和使用它們的最佳實踐。最後,你學習瞭如何把對象和數據記錄到垃圾收集日誌裏。

在這個教程裏,你已經學到了:

  • Java虛擬機的一些組件
  • G1垃圾收集器概覽
  • 回顧CMS收集器
  • 回顧G1收集器
  • 命令行開關和最佳實踐
  • G1收集器的日誌

參考:轉 G1垃圾收集器入門

  參考:G1 垃圾收集器入門

相關文章
相關標籤/搜索