## 轉自:http://blog.csdn.net/zhanggang807/article/details/45956325
[TOC]
最近在復習Java GC,因為G1比較新,JDK1.7才正式引入,比較艱難的找到一篇寫的很棒的文章,粘過來mark下。總結這篇文章和其他的資料,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 JVM`的`CMS`收集器如何工作。然后,一步一步來指導使用`Hotspot JVM`的`G1`垃圾回收,跟著,會用一段來講`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](http://www.oracle.com/technetwork/java/javase/downloads/index.html)。
* 從同樣的地址下載安裝演示示例壓縮文件,解壓文件到一個目錄中,比如:`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](http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-1.html)
### 探索Java虛擬機架構
#### Hotspot架構
`Hotspot`虛擬機擁有一個架構,它支持強大特性和能力的基礎平臺,支持實現高性能和強大的可伸縮性的能力。舉個例子,Hotspot虛擬機JIT編譯器生成動態的優化,換句話說,它們在Java應用執行期做出優化,為底層系統架構生成高性能的本地機器指令。另外,經過它的運行時環境和多線程垃圾回收成熟的進化和連續的設計,?`Hotspot`虛擬機在高可用計算系統上產出了高伸縮性。?

Java虛擬機的主要組件,包括類加載器、運行時數據區和執行引擎。
#### Hotspot關鍵組件
Java虛擬機有關性能的關鍵組件已經在下面的圖片上高亮顯示了。?

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)都把堆結構化為三個部分:年輕代、年老代和固定大小的永久代。?

所以內存對象最終都在這三個區域里。?
G1收集器應用了一個不同的方法。?

堆空間被分割成一些相同大小的堆區域,每一個都是連續范圍的虛擬內存。特定的區域集合像舊的收集器一樣被指派為相同的角色(伊甸: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應用線程運行時,并發的遍歷年老代對象圖可達的對象。從標記的對象和根上可達到標記對象開始掃描。設值方法在并發的2、3、5階段期間執行,在這些階段(包括晉升的對象)被分配進CMS代所有對象都會立刻被標記為活躍對象。 |
| (3)重新標記(Stop the World事件) | 尋找那些在并發標記階段丟失的,在并發收集器完成之后跟蹤對象之后由Java應用線程的更新的對象。 |
| (4)(并發清除) | 收集在標記階段被鑒定為不可達的對象。收集死亡對象會增加空閑列表的空間,方便之后的對象分配。聚合死亡對象可以會在此點發生。注意活躍對象是不會被移動。 |
| (5)(重新設置) | 清理數據結構為下一次并發收集做準備 |
#### 回顧垃圾收集步驟
接下來,讓我們一步一步的回顧下CMS收集器的操作步驟
##### 1\. CMS收集器堆結構
堆空間被分割為三塊空間。?

年輕代分割成一個Eden區和兩個Survivor區。年老代一個連續的空間。就地完成對象收集。除非有FullGC否則不會壓縮。
##### 2.CMS年輕代垃圾收集如何工作
年輕代被標為淺綠色,年老代被標記為藍色。如果你的應用已經運行了一段時間,CMS的堆看起來應該是這個樣子。對象分散在年老代區域里。?

使用CMS,年老代對象就地釋放。它們不會被來回移動。這個空間不會被壓縮除非發生FullGC。
##### 3.年輕代收集
從Eden和Survivor區復制活躍對象到另一個Survivor區。所有達到他們的年齡閾值的對象會晉升到年老代。?

##### 4.年輕代回收之后
一次年輕代垃圾收集之后,Eden區和其中一個Survivor區被清空。?

最近晉升的對象以深藍色顯示在上圖中,綠色的對象是年輕代幸免的還沒有晉升到老年代對象。
##### 5.CMS的年老代收集
發生兩次stop the world事件:初始標記和重新標記。當年老代達到特定的占用比例時,CMS開始執行。?

(1)初始標記是一個短暫暫停的、可達對象被標記的階段。(2)并發標記尋找活躍對象在應用連續執行時。最后,在(3)重新標記階段,尋找在之前并發標記階段中丟失的對象。
##### 6.年老代收集-并發清除
在之前階段沒有被標記的對象會被就地釋放。不進行壓縮操作。?

**注意:**未被標記的對象等于死亡對象
##### 7.年老代收集-清除之后
(4)清除階段之后,你可以看到大量內存被釋放。你還可以注意到沒有進行壓縮操作。?

最后,CMS收集器會走過(5)重新設置階段,等待下一次垃圾收集時機的到來。
## 循序漸進G1垃圾收集器
* * *
### 循序漸進G1垃圾收集器
G1收集器在分配堆空間的方法上有些不同。下面的圖片一步一步系統的回顧G1收集器。
#### 1.G1堆結構
堆空間是一個被分成許多固定大小區域的內存塊。?

Java虛擬機啟動時選定區域大小。Java虛擬機通常會指定2000個左右的大小相等、每個大小范圍在1到32M的區域。
#### 2.G1堆空間分配
實際上,這些區域被映射成Eden、Survivor、年老代空間的邏輯表述形式。?

圖片中的顏色表明了哪個區域被關聯上什么角色。活躍對象從一個區域疏散(復制、移動)到另一個區域。區域被設計為并行的方式收集,可以暫停或者不暫停所有的其它用戶線程。
明顯的區域可以被分配成Eden、Survivor、Old區域。另外,有第四種類型的區域叫做*極大區域(Humongous regions)*。這些區域被設計成保持標準區域大小的50%或者更大的對象。它們被保存在一個連續的區域集合里。最后,最后一個類型的區域就是堆空間里沒有使用的區域。
**注意:**寫作此文章時,收集極大對象時還沒有被優化。因此,你應該避免創建這個大小的對象。
#### 3.G1的年輕代
堆空間被分割成大約2000個區域。最小1M,最大32M,藍色區域保持年老代對象,綠色區域保持年輕代對象。?

**注意:**區域沒有必要像舊的收集器一樣是保持連續的。
#### 4.G1的年輕代收集
活躍對象會被疏散(復制、移動)到一個或多個survivor區域。如果達到晉升總閾值,對象會晉升到年老代區域。?

這是一個stop the world暫停。為下一次年輕代垃圾回收計算Eden和Survivor的大小。保留審計信息有助于計算大小。類似目標暫停時間的事情會被考慮在內。
這個方法使重調區域大小變得很容易,按需把它們調大或調小。
#### 5.G1年輕代回收的尾聲
活躍對象被疏散到Survivor或者年老代區域。?

最近晉升的對象顯示為深藍色。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)*?

#### 7.并發標記階段
如果發現空區域(“X”標示的),在重新標記階段它們會被馬上清除掉。當然,決定活性的審計信息也在此時被計算。?

#### 8.重新標記階段
空的區域被清除和回收掉。所有區域的活性在此時計算。?

#### 9.復制/清理階段
G1選擇活性最低的區域,這些區域能夠以最快的速度回收。然后這些區域會在年輕代垃圾回收過程中被回收。在日志中被指示為*\[GC pause (mixed)\]*。所以年輕代和年老代在同一時間被回收。?

#### 10.復制/清理階段之后
被選擇的區域已經被回收和壓縮到圖中顯示的深藍色區和深綠色區中。?

### 年老代垃圾回收總結
總結下,我們可以列出一些關于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](https://blogs.oracle.com/poonam/entry/understanding_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?
[此入門教程源地址](http://www.oracle.com/webfolder/technetwork/tutorials/obe/java/G1GettingStarted/index.html%2a%2a%E9%87%8D%E7%82%B9%E5%86%85%E5%AE%B9%2a%2a)上有一些介紹,但是和**Poonam Bajaj’s Blog post on G1 GC logs**內容幾乎相同,所以**詳細信息請看[我翻譯的Poonam Bajaj’s Blog post on G1 GC logs](http://blog.csdn.net/zhanggang807/article/details/46011341)**
## 總結
* * *
在這個`OBE`里,你已經對包含在Java虛擬機里的G1垃圾收集器有了大概的認識。首先你學習了為什么堆和垃圾收集器是任何Java虛擬機的關鍵部件。然后你回顧了使用CMS收集器和G1收集器的垃圾收集是如何工作的。然后你學習了關于G1收集器的命令行開關和使用它們的最佳實踐。最后,你學習了如何把對象和數據記錄到垃圾收集日志里。
在這個教程里,你已經學到了:
* Java虛擬機的一些組件
* G1垃圾收集器概覽
* 回顧CMS收集器
* 回顧G1收集器
* 命令行開關和最佳實踐
* G1收集器的日志
### 資源
更多相關信息請查看下面這些站點和鏈接:
* [HotSpot Java虛擬機選項](http://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html)
* [垃圾優先(G1)垃圾收集器](http://www.oracle.com/technetwork/java/javase/tech/g1-intro-jsp-135488.html)
* [Poonam Bajaj G1 GC Blog Post](https://blogs.oracle.com/poonam/entry/understanding_g1_gc_logs)
* [Java SE 7: 開發富客戶端應用](http://education.oracle.com/pls/web_prod-plq-dad/db_pages.getpage?page_id=609&p_org_id=1001&lang=US&get_params=dc:D67232GC10,p_preview:N)
* [Java性能 - Charlie Hunt and Binu John](http://www.amazon.com/Java-Performance-Charlie-Hunt/dp/0137142528/ref=sr_1_1)
* [Oracle Learning Library](http://www.oracle.com/oll)
### 致謝
* Curriculum Developer: Michael J Williams
* QA: Krishnanjani Chitta
- 一.JVM
- 1.1 java代碼是怎么運行的
- 1.2 JVM的內存區域
- 1.3 JVM運行時內存
- 1.4 JVM內存分配策略
- 1.5 JVM類加載機制與對象的生命周期
- 1.6 常用的垃圾回收算法
- 1.7 JVM垃圾收集器
- 1.8 CMS垃圾收集器
- 1.9 G1垃圾收集器
- 2.面試相關文章
- 2.1 可能是把Java內存區域講得最清楚的一篇文章
- 2.0 GC調優參數
- 2.1GC排查系列
- 2.2 內存泄漏和內存溢出
- 2.2.3 深入理解JVM-hotspot虛擬機對象探秘
- 1.10 并發的可達性分析相關問題
- 二.Java集合架構
- 1.ArrayList深入源碼分析
- 2.Vector深入源碼分析
- 3.LinkedList深入源碼分析
- 4.HashMap深入源碼分析
- 5.ConcurrentHashMap深入源碼分析
- 6.HashSet,LinkedHashSet 和 LinkedHashMap
- 7.容器中的設計模式
- 8.集合架構之面試指南
- 9.TreeSet和TreeMap
- 三.Java基礎
- 1.基礎概念
- 1.1 Java程序初始化的順序是怎么樣的
- 1.2 Java和C++的區別
- 1.3 反射
- 1.4 注解
- 1.5 泛型
- 1.6 字節與字符的區別以及訪問修飾符
- 1.7 深拷貝與淺拷貝
- 1.8 字符串常量池
- 2.面向對象
- 3.關鍵字
- 4.基本數據類型與運算
- 5.字符串與數組
- 6.異常處理
- 7.Object 通用方法
- 8.Java8
- 8.1 Java 8 Tutorial
- 8.2 Java 8 數據流(Stream)
- 8.3 Java 8 并發教程:線程和執行器
- 8.4 Java 8 并發教程:同步和鎖
- 8.5 Java 8 并發教程:原子變量和 ConcurrentMap
- 8.6 Java 8 API 示例:字符串、數值、算術和文件
- 8.7 在 Java 8 中避免 Null 檢查
- 8.8 使用 Intellij IDEA 解決 Java 8 的數據流問題
- 四.Java 并發編程
- 1.線程的實現/創建
- 2.線程生命周期/狀態轉換
- 3.線程池
- 4.線程中的協作、中斷
- 5.Java鎖
- 5.1 樂觀鎖、悲觀鎖和自旋鎖
- 5.2 Synchronized
- 5.3 ReentrantLock
- 5.4 公平鎖和非公平鎖
- 5.3.1 說說ReentrantLock的實現原理,以及ReentrantLock的核心源碼是如何實現的?
- 5.5 鎖優化和升級
- 6.多線程的上下文切換
- 7.死鎖的產生和解決
- 8.J.U.C(java.util.concurrent)
- 0.簡化版(快速復習用)
- 9.鎖優化
- 10.Java 內存模型(JMM)
- 11.ThreadLocal詳解
- 12 CAS
- 13.AQS
- 0.ArrayBlockingQueue和LinkedBlockingQueue的實現原理
- 1.DelayQueue的實現原理
- 14.Thread.join()實現原理
- 15.PriorityQueue 的特性和原理
- 16.CyclicBarrier的實際使用場景
- 五.Java I/O NIO
- 1.I/O模型簡述
- 2.Java NIO之緩沖區
- 3.JAVA NIO之文件通道
- 4.Java NIO之套接字通道
- 5.Java NIO之選擇器
- 6.基于 Java NIO 實現簡單的 HTTP 服務器
- 7.BIO-NIO-AIO
- 8.netty(一)
- 9.NIO面試題
- 六.Java設計模式
- 1.單例模式
- 2.策略模式
- 3.模板方法
- 4.適配器模式
- 5.簡單工廠
- 6.門面模式
- 7.代理模式
- 七.數據結構和算法
- 1.什么是紅黑樹
- 2.二叉樹
- 2.1 二叉樹的前序、中序、后序遍歷
- 3.排序算法匯總
- 4.java實現鏈表及鏈表的重用操作
- 4.1算法題-鏈表反轉
- 5.圖的概述
- 6.常見的幾道字符串算法題
- 7.幾道常見的鏈表算法題
- 8.leetcode常見算法題1
- 9.LRU緩存策略
- 10.二進制及位運算
- 10.1.二進制和十進制轉換
- 10.2.位運算
- 11.常見鏈表算法題
- 12.算法好文推薦
- 13.跳表
- 八.Spring 全家桶
- 1.Spring IOC
- 2.Spring AOP
- 3.Spring 事務管理
- 4.SpringMVC 運行流程和手動實現
- 0.Spring 核心技術
- 5.spring如何解決循環依賴問題
- 6.springboot自動裝配原理
- 7.Spring中的循環依賴解決機制中,為什么要三級緩存,用二級緩存不夠嗎
- 8.beanFactory和factoryBean有什么區別
- 九.數據庫
- 1.mybatis
- 1.1 MyBatis-# 與 $ 區別以及 sql 預編譯
- Mybatis系列1-Configuration
- Mybatis系列2-SQL執行過程
- Mybatis系列3-之SqlSession
- Mybatis系列4-之Executor
- Mybatis系列5-StatementHandler
- Mybatis系列6-MappedStatement
- Mybatis系列7-參數設置揭秘(ParameterHandler)
- Mybatis系列8-緩存機制
- 2.淺談聚簇索引和非聚簇索引的區別
- 3.mysql 證明為什么用limit時,offset很大會影響性能
- 4.MySQL中的索引
- 5.數據庫索引2
- 6.面試題收集
- 7.MySQL行鎖、表鎖、間隙鎖詳解
- 8.數據庫MVCC詳解
- 9.一條SQL查詢語句是如何執行的
- 10.MySQL 的 crash-safe 原理解析
- 11.MySQL 性能優化神器 Explain 使用分析
- 12.mysql中,一條update語句執行的過程是怎么樣的?期間用到了mysql的哪些log,分別有什么作用
- 十.Redis
- 0.快速復習回顧Redis
- 1.通俗易懂的Redis數據結構基礎教程
- 2.分布式鎖(一)
- 3.分布式鎖(二)
- 4.延時隊列
- 5.位圖Bitmaps
- 6.Bitmaps(位圖)的使用
- 7.Scan
- 8.redis緩存雪崩、緩存擊穿、緩存穿透
- 9.Redis為什么是單線程、及高并發快的3大原因詳解
- 10.布隆過濾器你值得擁有的開發利器
- 11.Redis哨兵、復制、集群的設計原理與區別
- 12.redis的IO多路復用
- 13.相關redis面試題
- 14.redis集群
- 十一.中間件
- 1.RabbitMQ
- 1.1 RabbitMQ實戰,hello world
- 1.2 RabbitMQ 實戰,工作隊列
- 1.3 RabbitMQ 實戰, 發布訂閱
- 1.4 RabbitMQ 實戰,路由
- 1.5 RabbitMQ 實戰,主題
- 1.6 Spring AMQP 的 AMQP 抽象
- 1.7 Spring AMQP 實戰 – 整合 RabbitMQ 發送郵件
- 1.8 RabbitMQ 的消息持久化與 Spring AMQP 的實現剖析
- 1.9 RabbitMQ必備核心知識
- 2.RocketMQ 的幾個簡單問題與答案
- 2.Kafka
- 2.1 kafka 基礎概念和術語
- 2.2 Kafka的重平衡(Rebalance)
- 2.3.kafka日志機制
- 2.4 kafka是pull還是push的方式傳遞消息的?
- 2.5 Kafka的數據處理流程
- 2.6 Kafka的腦裂預防和處理機制
- 2.7 Kafka中partition副本的Leader選舉機制
- 2.8 如果Leader掛了的時候,follower沒來得及同步,是否會出現數據不一致
- 2.9 kafka的partition副本是否會出現腦裂情況
- 十二.Zookeeper
- 0.什么是Zookeeper(漫畫)
- 1.使用docker安裝Zookeeper偽集群
- 3.ZooKeeper-Plus
- 4.zk實現分布式鎖
- 5.ZooKeeper之Watcher機制
- 6.Zookeeper之選舉及數據一致性
- 十三.計算機網絡
- 1.進制轉換:二進制、八進制、十六進制、十進制之間的轉換
- 2.位運算
- 3.計算機網絡面試題匯總1
- 十四.Docker
- 100.面試題收集合集
- 1.美團面試常見問題總結
- 2.b站部分面試題
- 3.比心面試題
- 4.騰訊面試題
- 5.哈羅部分面試
- 6.筆記
- 十五.Storm
- 1.Storm和流處理簡介
- 2.Storm 核心概念詳解
- 3.Storm 單機版本環境搭建
- 4.Storm 集群環境搭建
- 5.Storm 編程模型詳解
- 6.Storm 項目三種打包方式對比分析
- 7.Storm 集成 Redis 詳解
- 8.Storm 集成 HDFS 和 HBase
- 9.Storm 集成 Kafka
- 十六.Elasticsearch
- 1.初識ElasticSearch
- 2.文檔基本CRUD、集群健康檢查
- 3.shard&replica
- 4.document核心元數據解析及ES的并發控制
- 5.document的批量操作及數據路由原理
- 6.倒排索引
- 十七.分布式相關
- 1.分布式事務解決方案一網打盡
- 2.關于xxx怎么保證高可用的問題
- 3.一致性hash原理與實現
- 4.微服務注冊中心 Nacos 比 Eureka的優勢
- 5.Raft 協議算法
- 6.為什么微服務架構中需要網關
- 0.CAP與BASE理論
- 十八.Dubbo
- 1.快速掌握Dubbo常規應用
- 2.Dubbo應用進階
- 3.Dubbo調用模塊詳解
- 4.Dubbo調用模塊源碼分析
- 6.Dubbo協議模塊