一:了解G1
1:定义
G1 GC(垃圾优先型垃圾回收器)适用于Java HotSpotVM 的低停顿、面向服务器的高可用分代式垃圾回收器。
G1 GC使用并发和并行阶段实现其目标暂停时间,并保持良好的吞吐量。G1在jdk7u04中作为正式版发布,在jdk6U14中作为测试版发布,
jdk6使用G1需要使用-XX:+UnlockExperimentalVMOptions 参数打开,jdk6的很多G1调整参数被调整不建议使用.
二:G1的适用场景和特点
1:适用场景
1)适用在big heap(6GB or larger)场景,不能容忍长时间停顿(Full GC)的服务器场景.
2)有大量存活对象(超过50%)的场景.例如:数据服务器场景.Hbase,Cassandra等.
3)解决传统的parNew+CMS垃圾收集算法中,有很多晋代失败(promotion failed)/并发失败(concurrent mode failure),然后做Full gc..
2:特点
1)G1可以控制最长停顿时间(-XX:MaxGCPauseMillis),这是个预计值,G1会根据这个时间制定回收策略,但是设置过低停顿回造成低吞吐。
2)G1基于复制的算法,回收过程不会产生碎片,这是相比CMS最大的优势.
3)G1把heap分割成很多大小相等的区域(region),不再使用固定空间分代的方式,采用逻辑分代:Eden,Survivor,Old。每个逻辑分代含有若干区域且数量可以动态调整.
通过使用jmap -heap 得到的堆统计信息可以更好的理解逻辑分代:
Heap Configuration:
MinHeapFreeRatio =
40
MaxHeapFreeRatio =
70
MaxHeapSize =
25769803776
(
24576
.0MB)
NewSize =
1363144
(
1
.2999954223632812MB)
MaxNewSize =
17592186044415
MB
OldSize =
5452592
(
5
.1999969482421875MB)
NewRatio =
2
SurvivorRatio =
6
PermSize =
100663296
(
96
.0MB)
MaxPermSize =
100663296
(
96
.0MB)
G1HeapRegionSize =
8388608
(
8
.0MB)
G1 Heap:
regions =
3072
capacity =
25769803776
(
24576
.0MB)
used =
11232244096
(
10711
.902709960938MB)
free =
14537559680
(
13864
.097290039062MB)
43.58684370915095
% used
G1 Young Generation:
Eden Space:
regions =
29
capacity =
1233125376
(
1176
.0MB)
used =
243269632
(
232
.0MB)
free =
989855744
(
944
.0MB)
19.727891156462587
% used
Survivor Space:
regions =
14
capacity =
117440512
(
112
.0MB)
used =
117440512
(
112
.0MB)
free =
0
(
0
.0MB)
100.0
% used
G1 Old Generation:
regions =
1298
capacity =
24419237888
(
23288
.0MB)
used =
10871533952
(
10367
.902709960938MB)
free =
13547703936
(
12920
.097290039062MB)
44.52036546702567
% used
|
三:理解G1的运行过程
1:G1的Heap划分
G1 GC 启动后把堆被划分成大小相同的多个区域(Region)。
区域的大小由(-XX:G1HeapRegionSize)指定,不指定区域大小会从1MB到32MB自动制定,区域大小确定后不会做动态调整.
整个heap划分为Eden,Survivor,Old三个不连续的逻辑区域。
如下图标示G1对整个heap的划分
图一
运行过程中,G1会为每个Region使用Remembered Set来维护对象引用,
应用程序在对Reference类型的数据进行写操作时会产生一个Write Barrier暂时中断写操作,
检查Reference引用的对象是否处于不同的Region之中,如果是,便通过CardTable把相关引用信息记录到被引用对象所属的Region的Remembered Set之中。
当进行内存回收时,在GC根节点的枚举范围中加入Remembered Set来避免对全堆扫描.
2:G1收集器运行:标记,回收.
1)标记:
初始标记阶段:此阶段,G1GC对根进行标记。该阶段与年轻代垃圾(STW)回收密切相关。
根区域扫描阶段:G1GC 在初始标记的存活区扫描对老年代的引用,并标记被引用的对象.该阶段与应用程序同时运行,该阶段完成后,才能开始下一次年轻代垃圾回收。
并发标记阶段:G1GC 在整个堆中查找存活对象。该阶段与应用程序同时运行,可以被年轻代垃圾回收中断。
重新标记阶段:该阶段是 STW 回收,帮助完成标记周期。G1 GC 清空 SATB 缓冲区,跟踪未被访问的存活对象,并执行引用处理。
清理阶段:在这个最后阶段,G1 GC 执行统计和 RSet 清理的STW操作。在统计期间,G1GC 会识别完全空闲的区域和可供进行混合垃圾回收的区域。
清理阶段会将空白区域重置并返回到空闲列表。
2)回收:
回收过程按回收区域集合(CollectionSets/CSets)划分.CSets可以包含(Eden,survivor/old)中的region,CSets占用内存一般小于整个jvm的1%;
年轻代垃圾回收:
G1 Young GC同时回收eden区域和上次垃圾回收的存活区域。Eden 和survivor的存活对象被疏散(复制/移动)到新的区域集。
被疏散对象的目标区域取决于对象的年龄,达到晋升年龄的对象疏散到老年代区域,
否则疏散到存活区,并将它包含在下一次年轻代或混合垃圾回收的CSet中。
混合垃圾回收:
G1 Mixd GC同时回收Eden,survivor,old的存活区域。当成功完成并发标记周期后,G1 GC从执行年轻代垃圾回收切换为执行混合垃圾回收。
在混合垃圾回收期间,G1GC 将一些旧的区域添加到 eden 和存活区供将来回收。G1GC 回收了足够的旧区域后(经过多次混合垃圾回收),
G1将恢复执行年轻代垃圾回收,直到下一次标记周期完成。
G1GC不同阶段暂定和描述总结
如下表:
Phase | Description |
(1) Initial Mark (Stop the World Event) |
This is a stop the world event. With G1, it is piggybacked on a normal young GC. Mark survivor regions (root regions) which may have references to objects in old generation. |
(2) Root Region Scanning | Scan survivor regions for references into the old generation. This happens while the application continues to run. The phase must be completed before a young GC can occur. |
(3) Concurrent Marking | Find live objects over the entire heap. This happens while the application is running. This phase can be interrupted by young generation garbage collections. |
(4) Remark (Stop the World Event) |
Completes the marking of live object in the heap. Uses an algorithm called snapshot-at-the-beginning (SATB) which is much faster than what was used in the CMS collector. |
(5) Cleanup (Stop the World Event and Concurrent) |
|
(*) Copying (Stop the World Event) |
These are the stop the world pauses to evacuate or copy live objects to new unused regions. This can be done with young generation regions which are logged as [GC pause (young)] . Or both young and old generation regions which are logged as [GC Pause (mixed)] . |
四:G1相关参数
-XX:G1HeapRegionSize=n
设置的 G1 区域的大小。值是 2 的幂,范围是 1 MB 到 32 MB 之间。目标是根据最小的 Java 堆大小划分出约 2048 个区域。
-XX:MaxGCPauseMillis=200
为所需的最长暂停时间设置目标值。默认值是 200 毫秒。指定的值不适用于您的堆大小。
-XX:ParallelGCThreads=cpus
设置年轻代垃圾回收(STW)工作线程数的值。将 n 的值设置为逻辑处理器的数量。n 的值与逻辑处理器的数量相同,G1根据cpu数量设置不同的初始值。
-XX:ConcGCThreads=cpus/4
设置并发标记的线程数。将n设置为并行垃圾回收线程数 (ParallelGCThreads) 的 1/4 左右。
-XX:InitiatingHeapOccupancyPercent=45
设置触发标记周期的Java堆占用率阈值。默认占用率是整个 Java 堆的 45%。
-XX:G1HeapWastePercent=10
设置G1愿意浪费的堆百分比。如果可回收百分比小于堆废物百分比。默认值是 10%。
-XX:G1MixedGCCountTarget=8
设置标记周期完成后,对旧区域执行混合垃圾回收的目标次数。默认值是 8 次混合垃圾回收。
混合回收的目标是控制在此目标次数以内。
-XX:G1ReservePercent=10
设置作为空闲空间的预留内存百分比,以降低目标空间溢出的风险。默认值是 10%。增加或减少百分比时,需要评估Java堆的量。
无:日志分析
Poonam Bajaj 大神写了G1的日志格式分析 , 直接引用她的: Understanding_G1_GC_LOGS
六:优化建议
1:不要使用-Xmn/-XX:NewRatio等设置年轻代大小,G1使用逻辑分代,年轻代大小是动态调整的.且固定年轻代的大小会覆盖暂停时间目标
2:暂停时间目标(-XX:MaxGCPauseMillis):设置所需的软实时目标,G1GC 会尽量满足.当对垃圾回收进行评估或调优时,都会涉及到延迟与吞吐量的取舍。需要G1高吞吐量时,暂停时间目标不要太严苛。
3:调优混合垃圾回收时,需合理调整以下参数:
-XX:InitiatingHeapOccupancyPercent 用于更改标记阈值。更小的值意味着更快的触发标记周期。
-XX:G1MixedGCCountTarget 用于调整Old区域的CSet集合。
4:G1溢出/耗尽引发的FullGc
2014-03-06T10:01:28.216+0800: 67998.279: [GC pause (mixed) (to-space exhausted), 13.8681600 secs]
2014-03-04T10:01:42.086+0800: 68012.149: [GC pause (mixed) (to-space overflow), 3.0012670 secs]
优化参数:
增加-XX:G1ReservePercent 选项的值(并相应增加总的堆大小,为"目标空间"预留更多内存空间。
减少 -XX:InitiatingHeapOccupancyPercent 提前启动标记周期。
增加 -XX:ConcGCThreads 选项的值来增加并行标记线程的数目,加快标记速度。
5:避免巨型对象
G1对于任何超过区域一半大小的对象都被视为“巨型对象”,巨型区域直接被分配到老年代中.
巨型区域是一个连续的区域集。StartsHumongous 标记该连续集的开始,ContinuesHumongous 标记它的延续.
所以没有使用的巨型对象的终点与上个区域的终点之间的空间无法被使用,
如果对象只是略大于堆区域大小的倍数,未使用的空间可能会导致堆碎片化。
巨型对象也可能分配导致连续的并发周期,并且此类分配导致老年代碎片化
通过增加-XX:G1HeapRegionSize的值,提高巨型对象的门槛(G1HeapRegionSize/2)。