JVM基础(6)——G1收集器及G1日志分析
2016-11-15 12:57
148 查看
系列文章规划:
JVM基础(1)——内存模型
JVM基础(2)——内存管理
JVM基础(3)——编译机制
JVM基础(4)——类加载机制
JVM基础(5)——垃圾回收和调优
JVM基础(6)——G1收集器及G1日志分析
JVM基础(7)——jdk常用内置工具
在极大概率满足GC停顿时间要求的同时,还具备高吞吐量的特性.。Oracle JDK 7 update 4 及以上版本已完全支持。其设计目标如下:
可以像CMS收集器一样,GC操作与应用的线程一起并发执行;
紧凑的空闲内存区间且没有很长的GC停顿时间;
需要更多可预测的GC停顿时间;
不想牺牲太多吞吐量性能;
启动后不需要请求更大的Java堆。
G1的长期目标是取代CMS(Concurrent Mark-Sweep Collector, 并发标记-清除)。因为特性的不同,G1成为比CMS更好的解决方案。其中一个区别是,G1是一款压缩型的收集器。G1通过有效的压缩完全避免了对细微空闲内存空间的分配,不用依赖于regions,这不仅大大简化了收集器,而且还消除了潜在的内存碎片问题。除压缩以外,G1的垃圾收集停顿也比CMS容易估计,也允许用户自定义所希望的停顿参数(pause targets)。
内存占用
如果从 ParallelOldGC 或者 CMS收集器迁移到 G1,可能会看到JVM进程占用更多的内存(a larger JVM process size)。 这在很大程度上与 “accounting” 数据结构有关,如 Remembered Sets 和 Collection Sets。
Remembered Sets 简称 RSets。跟踪指向某个heap区内的对象引用。 堆内存中的每个区都有一个 RSet。 RSet 使heap区能并行独立地进行垃圾集合。 RSets的总体影响小于5%。
Collection Sets 简称 CSets。收集集合, 在一次GC中将执行垃圾回收的heap区。GC时在CSet中的所有存活数据(live data)都会被转移(复制/移动)。集合中的heap区可以是 Eden, survivor, 和/或 old generation。CSets所占用的JVM内存小于1%。
推荐使用G1的场景
G1的首要目标是为需要大量内存的系统提供一个保证GC低延迟的解决方案。 也就是说堆内存在6GB及以上,稳定和可预测的暂停时间小于0.5秒。
如果应用程序具有如下的一个或多个特征,那么将垃圾收集器从CMS或ParallelOldGC切换到G1将会大大提升性能。
Full GC 次数太频繁或者消耗时间太长.
对象分配的频率或代数提升(promotion)显著变化.
受够了太长的垃圾回收或内存整理时间(超过0.5~1秒)
注意: 如果正在使用CMS或ParallelOldGC,而应用程序的垃圾收集停顿时间并不长,那么建议继续使用现在的垃圾收集器。使用最新的JDK时,并不要求切换到G1收集器。
年轻代由非连续区组成,可以很方便的进行年轻代大小修改。
新对象进入Eden区
存活对象拷贝到Survivor区;
存活时间达到年龄阈值时,对象晋升到Old区。
young gc是多线程并行执行的。
初始并行阶段(Initial Marking Phase)
Root区扫描(Root Region Scanning)
并行标记(Concurrent Marking)
重标记(Remark)
清除(Cleanup)
复制(Copying)
初始并行阶段(Initial Marking Phase)
属于Young GC范畴,是stop-the-world活动。对持有老年代对象引用的Survivor区(Root区)进行标记。
Root区扫描(Root Region Scanning)
扫描Survivor区中的老年代对象引用,该阶段发生在应用运行时,必须在Young GC前完成。
并行标记(Concurrent Marking)
找出整个堆中存活的对象,对于空区标记为“X”。该阶段发生在应用运行时,同时该阶段活动会被Young GC打断。
重标记(Remark)
清除空区,重计算所有区的存活状态(liveness),是stop-the-world活动。
清除(Cleanup)
选择出存活状态低的区进行收集。
计算存活对象和空区,是stop-the-world活动。
更新记录表,是stop-the-world活动。
重置空区,将其加入空闲列表,是并行活动。
复制(Copying)
该阶段是stop-the-world活动,负责将存活对象复制到新的未使用的区。
可以发生在年轻代区,日志记录为[GC pause (young)]。
也可以同时发生在年轻代区和老年代区,日志记录为[GC Pause (mixed)]。
G1在垃圾收集时将不再关心暂停时间指标. 所以从本质上说,设置年轻代的大小将禁用暂停时间目标.
G1在必要时也不能够增加或者缩小年轻代的空间. 因为大小是固定的,所以对更改大小无能为力.
GC仍继续所以空间必须被释放,拷贝失败的对象必须被放到正确的位置(tenured in place),CSet指向区域中的任何 RSets 更新都必须重新生成(regenerated),所有这些步骤都是代价高昂的。
增加堆内存大小
增加 -XX:G1ReservePercent=n, 其默认值是 10.
G1创建了一个假天花板(false ceiling),在需要更大 ‘to-space’ 的情况下会尝试从保留内存获取(leave the reserve memory free).
更早启动标记周期(marking cycle)
通过采用 -XX:ConcGCThreads=n 选项增加标记线程(marking threads)的数量.
-verbosegc。和-XX:+PrintGC等效,日志级别为fine。
-XX:+PrintGCDetails。日志级别为finer,包含的信息比较完整。
-XX:+UnlockExperimentalVMOptions -XX:G1LogLevel=finest。日志级别为finest,包含finer信息以及每个工作线程的信息。
2种时间戳格式:
-XX:+PrintGCTimeStamps。毫秒时间。
-XX:+PrintGCDateStamps。日期时间。
-XX:+PrintGCDetails日志示例:
Garbage First Garbage Collector Tuning
Java HotSpot Garbage Collection
Understanding G1 GC Logs
JVM基础(1)——内存模型
JVM基础(2)——内存管理
JVM基础(3)——编译机制
JVM基础(4)——类加载机制
JVM基础(5)——垃圾回收和调优
JVM基础(6)——G1收集器及G1日志分析
JVM基础(7)——jdk常用内置工具
1 G1简介
1.1 概述
G1 (Garbage-First)是一款面向服务器的垃圾收集器,主要针对配备多核处理器及大容量内存的机器。在极大概率满足GC停顿时间要求的同时,还具备高吞吐量的特性.。Oracle JDK 7 update 4 及以上版本已完全支持。其设计目标如下:
可以像CMS收集器一样,GC操作与应用的线程一起并发执行;
紧凑的空闲内存区间且没有很长的GC停顿时间;
需要更多可预测的GC停顿时间;
不想牺牲太多吞吐量性能;
启动后不需要请求更大的Java堆。
G1的长期目标是取代CMS(Concurrent Mark-Sweep Collector, 并发标记-清除)。因为特性的不同,G1成为比CMS更好的解决方案。其中一个区别是,G1是一款压缩型的收集器。G1通过有效的压缩完全避免了对细微空闲内存空间的分配,不用依赖于regions,这不仅大大简化了收集器,而且还消除了潜在的内存碎片问题。除压缩以外,G1的垃圾收集停顿也比CMS容易估计,也允许用户自定义所希望的停顿参数(pause targets)。
内存占用
如果从 ParallelOldGC 或者 CMS收集器迁移到 G1,可能会看到JVM进程占用更多的内存(a larger JVM process size)。 这在很大程度上与 “accounting” 数据结构有关,如 Remembered Sets 和 Collection Sets。
Remembered Sets 简称 RSets。跟踪指向某个heap区内的对象引用。 堆内存中的每个区都有一个 RSet。 RSet 使heap区能并行独立地进行垃圾集合。 RSets的总体影响小于5%。
Collection Sets 简称 CSets。收集集合, 在一次GC中将执行垃圾回收的heap区。GC时在CSet中的所有存活数据(live data)都会被转移(复制/移动)。集合中的heap区可以是 Eden, survivor, 和/或 old generation。CSets所占用的JVM内存小于1%。
推荐使用G1的场景
G1的首要目标是为需要大量内存的系统提供一个保证GC低延迟的解决方案。 也就是说堆内存在6GB及以上,稳定和可预测的暂停时间小于0.5秒。
如果应用程序具有如下的一个或多个特征,那么将垃圾收集器从CMS或ParallelOldGC切换到G1将会大大提升性能。
Full GC 次数太频繁或者消耗时间太长.
对象分配的频率或代数提升(promotion)显著变化.
受够了太长的垃圾回收或内存整理时间(超过0.5~1秒)
注意: 如果正在使用CMS或ParallelOldGC,而应用程序的垃圾收集停顿时间并不长,那么建议继续使用现在的垃圾收集器。使用最新的JDK时,并不要求切换到G1收集器。
1.2 G1内存结构
G1堆由多个区(region)组成,每个区大小1M~32M,逻辑上区有3种类型,包括(Eden、Survivor、Old),按分代划分包括:年轻代(Young Generation)和老年代(Old Generation)。年轻代由非连续区组成,可以很方便的进行年轻代大小修改。
1.3 Young GC
Young GC是stop-the-world活动,会导致整个应用线程的停止。其过程如下:新对象进入Eden区
存活对象拷贝到Survivor区;
存活时间达到年龄阈值时,对象晋升到Old区。
young gc是多线程并行执行的。
1.4 Old GC
Old GC包含以下阶段(其中有些阶段是属于Young GC的):初始并行阶段(Initial Marking Phase)
Root区扫描(Root Region Scanning)
并行标记(Concurrent Marking)
重标记(Remark)
清除(Cleanup)
复制(Copying)
初始并行阶段(Initial Marking Phase)
属于Young GC范畴,是stop-the-world活动。对持有老年代对象引用的Survivor区(Root区)进行标记。
Root区扫描(Root Region Scanning)
扫描Survivor区中的老年代对象引用,该阶段发生在应用运行时,必须在Young GC前完成。
并行标记(Concurrent Marking)
找出整个堆中存活的对象,对于空区标记为“X”。该阶段发生在应用运行时,同时该阶段活动会被Young GC打断。
重标记(Remark)
清除空区,重计算所有区的存活状态(liveness),是stop-the-world活动。
清除(Cleanup)
选择出存活状态低的区进行收集。
计算存活对象和空区,是stop-the-world活动。
更新记录表,是stop-the-world活动。
重置空区,将其加入空闲列表,是并行活动。
复制(Copying)
该阶段是stop-the-world活动,负责将存活对象复制到新的未使用的区。
可以发生在年轻代区,日志记录为[GC pause (young)]。
也可以同时发生在年轻代区和老年代区,日志记录为[GC Pause (mixed)]。
2 配置G1
参数/默认值 | 含义 |
---|---|
-XX:+UseG1GC | 使用 G1 垃圾收集器 |
-XX:MaxGCPauseMillis=200 | 设置期望达到的最大GC停顿时间指标(JVM会尽力实现,但不保证达到) |
-XX:InitiatingHeapOccupancyPercent=45 | 启动并发GC周期时的堆内存占用百分比. G1之类的垃圾收集器用它来触发并发GC周期,基于整个堆的使用率,而不只是某一代内存的使用比. 值为 0 则表示”一直执行GC循环”. 默认值为 45. |
-XX:NewRatio=n | 新生代与老生代(new/old generation)的大小比例(Ratio). 默认值为 2. |
-XX:SurvivorRatio=n | eden/survivor 空间大小的比例(Ratio). 默认值为 8. |
-XX:MaxTenuringThreshold=n | 提升年老代的最大临界值(tenuring threshold). 默认值为 15. |
-XX:ParallelGCThreads=n | 设置垃圾收集器在并行阶段使用的线程数,默认值随JVM运行的平台不同而不同. |
-XX:ConcGCThreads=n | 并发垃圾收集器使用的线程数量. 默认值随JVM运行的平台不同而不同. |
-XX:G1ReservePercent=n | 设置堆内存保留为假天花板的总量,以降低提升失败的可能性. 默认值是 10. |
-XX:G1HeapRegionSize=n | 使用G1时Java堆会被分为大小统一的的区(region)。此参数可以指定每个heap区的大小. 默认值将根据 heap size 算出最优解. 最小值为 1Mb, 最大值为 32Mb. |
3 最佳实践
在使用 G1 作为垃圾收集器时,你应该遵循下面这些最佳实践的指导.3.1 不要设置年轻代的大小(Young Generation Size)
假若通过 -Xmn 显式地指定了年轻代的大小, 则会干扰到 G1收集器的默认行为.G1在垃圾收集时将不再关心暂停时间指标. 所以从本质上说,设置年轻代的大小将禁用暂停时间目标.
G1在必要时也不能够增加或者缩小年轻代的空间. 因为大小是固定的,所以对更改大小无能为力.
3.2 响应时间指标(Response Time Metrics)
设置 XX:MaxGCPauseMillis= 时不应该使用平均响应时间(ART, average response time) 作为指标,而应该考虑使用目标时间的90%或者更大作为响应时间指标。也就是说90%的用户请求响应时间不会超过预设的目标值. 记住,暂停时间只是一个目标,并不能保证总是得到满足。3.3 什么是转移失败(Evacuation Failure)?
对 survivors 或 promoted objects 进行GC时,如果JVM的heap区不足,就会发生提升失败(promotion failure)。堆内存不能继续扩充,因为已经达到最大值了。当使用 -XX:+PrintGCDetails 时将会在GC日志中显示 to-space overflow (to-空间溢出)。GC仍继续所以空间必须被释放,拷贝失败的对象必须被放到正确的位置(tenured in place),CSet指向区域中的任何 RSets 更新都必须重新生成(regenerated),所有这些步骤都是代价高昂的。
3.4 如何避免转移失败(Evacuation Failure)
要避免避免转移失败, 考虑采纳下列选项:增加堆内存大小
增加 -XX:G1ReservePercent=n, 其默认值是 10.
G1创建了一个假天花板(false ceiling),在需要更大 ‘to-space’ 的情况下会尝试从保留内存获取(leave the reserve memory free).
更早启动标记周期(marking cycle)
通过采用 -XX:ConcGCThreads=n 选项增加标记线程(marking threads)的数量.
4 日志分析
G1的调优需要利用日志信息,但首先我们需要收集到日志并能理解日志信息。4.1 日志格式
3种日志详情格式:-verbosegc。和-XX:+PrintGC等效,日志级别为fine。
-XX:+PrintGCDetails。日志级别为finer,包含的信息比较完整。
-XX:+UnlockExperimentalVMOptions -XX:G1LogLevel=finest。日志级别为finest,包含finer信息以及每个工作线程的信息。
2种时间戳格式:
-XX:+PrintGCTimeStamps。毫秒时间。
-XX:+PrintGCDateStamps。日期时间。
-XX:+PrintGCDetails日志示例:
0.522: [GC pause (young), 0.15877971 secs] // 程序运行0.522秒后发生一个Evacuation Pause,耗时0.15877971秒。 [Parallel Time: 157.1 ms] // 并行GC耗时157.1 ms [GC Worker Start (ms): 522.1 522.2 522.2 522.2 // 每个工作线程启动时间,时间根据线程id排序 Avg: 522.2, Min: 522.1, Max: 522.2, Diff: 0.1] // 所有工作线程启动时间的平均值、最小值、最大值、差别 [Ext Root Scanning (ms): 1.6 1.5 1.6 1.9 // 每个扫描root的线程耗时 Avg: 1.7, Min: 1.5, Max: 1.9, Diff: 0.4] [Update RS (ms): 38.7 38.8 50.6 37.3 // 每个执行更新RS(Remembered Sets)的线程的耗时 Avg: 41.3, Min: 37.3, Max: 50.6, Diff: 13.3] [Processed Buffers : 2 2 3 2 // 每个工作线程执行UB(Update Buffers)的数量 Sum: 9, Avg: 2, Min: 2, Max: 3, Diff: 1] [Scan RS (ms): 9.9 9.7 0.0 9.7 // 每个工作线程扫描RS的耗时 Avg: 7.3, Min: 0.0, Max: 9.9, Diff: 9.9] [Object Copy (ms): 106.7 106.8 104.6 107.9 // 每个工作线程执行OC(Object Copy)的耗时 Avg: 106.5, Min: 104.6, Max: 107.9, Diff: 3.3] [Termination (ms): 0.0 0.0 0.0 0.0 // 每个工作线程执行终止的耗时 Avg: 0.0, Min: 0.0, Max: 0.0, Diff: 0.0] [Termination Attempts : 1 4 4 6 // 每个工作线程执行终止的重试的次数 Sum: 15, Avg: 3, Min: 1, Max: 6, Diff: 5] [GC Worker End (ms): 679.1 679.1 679.1 679.1 // 每个工作线程终止时的时间 Avg: 679.1, Min: 679.1, Max: 679.1, Diff: 0.1] [GC Worker (ms): 156.9 157.0 156.9 156.9 // 每个工作线程的生命时间 Avg: 156.9, Min: 156.9, Max: 157.0, Diff: 0.1] [GC Worker Other (ms): 0.3 0.3 0.3 0.3 // 每个工作线程执行其他任务(上述未统计的内容)的耗时 Avg: 0.3, Min: 0.3, Max: 0.3, Diff: 0.0] [Clear CT: 0.1 ms] // 清理CT(Card Table)的耗时 [Other: 1.5 ms] // 其他任务(上述未统计的内容)的耗时 [Choose CSet: 0.0 ms] // 选择分区的耗时 [Ref Proc: 0.3 ms] // 执行关联(Reference objects)的耗时 [Ref Enq: 0.0 ms] // 将references放入ReferenceQueues的耗时 [Free CSet: 0.3 ms] // 释放CS(collection set)的耗时 [Eden: 12M(12M)->0B(10M) Survivors: 0B->2048K Heap: 13M(64M)->9739K(64M)] // Eden容量为12M,使用了12M,GC后变为0,容量目标大小增加到13M。 // SurvivorsGC前为0,GC后变为2048K // GC前,Heap容量为64M,使用13M;GC后,Heap容量为64M,使用9739k。 [Times: user=0.59 sys=0.02, real=0.16 secs]
5 调优
//TODO参考文档
Getting Started with the G1 Garbage CollectorGarbage First Garbage Collector Tuning
Java HotSpot Garbage Collection
Understanding G1 GC Logs
相关文章推荐
- RHEL 5基础篇—分析系统日志
- iOS应用的crash日志的分析基础
- JVM Crash 日志(hs_err_pid.log)分析
- 日志分析-1.rsyslog 基础配置(服务器/客户端)
- JVM基础:生产环境参数实例及分析
- iOS应用的crash日志的分析基础
- Java基础之jdk1.8 JVM内存模型简述,含String常量池简单分析
- HotSpot的G1收集器(日志分析)
- iOS应用的crash日志的分析基础
- jvm gc日志分析
- Java性能分析及问题解决(二)jvm致命错误导致进程直接挂掉,错误日志分析及解决
- 【Apache运维基础(6)】Apache的日志管理与分析
- JVM的GC日志分析
- 【码云周刊第 37 期】如何从运维零基础练好日志分析与统计?
- 记一次JVM GC日志分析(中间给出了部分的JVM配置参数)
- 如何从运维零基础练好日志分析?
- jvm的GC日志分析
- JVM调优日志解析分析
- nginx之基础命令(日志分析)
- iOS应用的crash日志的分析基础