谈谈你的GC调优思路?

上一篇专栏简单介绍了一下GC,使我们对Oracle JDK的GC有一定的了解,有Serial GC这种古老简单的单线程计算模式,也有CMS并行计算收集机制,还有新型调优思路G1 GC。

之后我们又介绍了单线程集中式的GC流程。简单来说,就是程序运行的过程中,对当前周期使用到堆内部的对象实训,进行标记,其他进行清除不断循坏,并且在这个过程中进行一定的内存管理。最后成为老年代。

而老年代的GC不论是诞生的过程和在这之后GC,都与使用的具体GC使用策略有关。

那么,既然我们已经知道了这个流程了,下面就来和大家说一下GC的调优思路。

概述

现在要明确一件事:调优的目标,究竟是为什么?
首先,从性能角度来分析,通常关注三个方面:

  1. 内存占用(footprint)
  2. 延时(latency)
  3. 吞吐量(throughput)

大多数情况下调优会侧重于其中1-2个角度去调优,很少有三个方面全部考虑的。

基本的调优思路可以总结为:

  • 理解针对应用的需求和问题,确定调优目标。假设我们开发了一个产品,经常会出现性能抖动、延迟的问题,那么再评估完用户需求之后,就需要对于GC的暂停,有一个量化的控制,同时保证一定的吞吐量。
  • 掌握JVM和GC的状态,定位具体的问题,确定自己真的需要GC调优。方法有很多。比如使用jstat查看GC状态,开启GC日志,利用操作系统提供的诊断工具等等。
  • 选择的GC类型是否符合应用场景,比如Minor GC会异常停顿,CMS和G1更适合低延迟。
  • 分析得出要调整的的参数或者软硬件配置
  • 验证是否达到调优目标,如果达到目标,即可以考虑结束调优;否则重复完成分析、调整、验证这个过程。

分析思路

GC调优是JVM调优的一个基础,有很多JVM调优的需求,最后都会落实在GC上或者与其相关。我们不仅仅要知道这些理论层面的知识,还要有项目中产生的直觉,可以从网上找到一些联系的机会,在专栏中会更侧重关于思路。

这次,围绕G1 GC来讲,接下来主要分析下面两点:

  • 由于G1 GC是现在时代的首选,所以在手机GC日志方面有了很大的改善。会说说具体内部结果和主要机制。
  • 从调优实践的角度,理解通用的调优思路。

知识扩展

首先,来了解一下G1 GC的内部结构和主要机制。

G1内部同样存在年代的概念,但是内部结构编程了region:
在这里插入图片描述
每一个region的大小是一致的,但是数值在1-32MB之间的一个2的幂指数,JVM会尽量划分2048个同等大小的region,当然这个数字也会手动调整。

在G1实现中,年代是一个逻辑概念,具体体现在,一部分region是作为Eden,一部分Survivor,除了意料中的老年代,G1会将一半多的在堆内的对象都归类为Humongous对象,并且放置在region中。从逻辑上来说复制超大规模的对象是很消耗性能,必然不是新生代的GC算法,所以也算是老年代一部分。

那么,region的设计有什么副作用吗?

例如,其大小均等的划分看似很厉害,但是实际上,大对象大于一个region放不进去,小对象放进去浪费,这就是JVM设计的问题所在,尽管解决问题很简单,那就是直接设置比较大的region大小,参数如下:
-XX:G1HeapRegionSize=<N, 例如16>M

从GC算法的角度来说,G1所谓的符合算法们可以简单理解为:

  • 在新生代,G1采用的仍然是并行复制算法,所以同样会发生Stop-The-World的暂停。
  • 在老年代,大部分情况下都是并发标记,而整理的老年代与新生代同时的,所以整理是增量进行的。

从习惯上来讲,新生代GC被叫做Minor GC,老年代GC被叫做Major GC,区别于整体性的Full GC。 但是现代GC中,这种概念已经不再准确,对于G1来说:

  • Minor GC仍然存在,但是会涉及到Remembered Set等相关处理。
  • 老年代GC,则是依靠Mixed GC。意思是并发标记结束后,Mixed GC会进行clean操作,清理Eden Survivor,还会清理部分old区域。通过设置阈值来设定最多包含在一次Mixed GC的region比例。

–XX:G1MixedGCLiveThresholdPercent –XX:G1OldCSetRegionThresholdPercent

从G1内部运行的角度来说,下面是运行图但是当逃逸失败的等情况,会触发Full GC。
在这里插入图片描述
然后说一下Remembered Set操作究竟是什么

关键在于记录与维护region之间对象的引用关系,保证在Eden/Survivor -> to区域的对象移动复制操作,跨区引用也依然有效。
在这里插入图片描述
G1的很多开销源自于Remembered Set,由于它的迁移对象,会导致占用堆自身内存20%的情况或者更高,影响了复制的速度,进而影响暂停时间

接下来,我介绍下大家可能还不了解的G1行为变化,它们在一定程度上解决了专栏其他讲中提到的部分困扰,如类型卸载不及时的问题。

  • 在旧版G1中,Humongous通常都是在并发标记之后才会开始进行回收,这就会造成一个的内存问题,但是在新版G1中,因为时刻会记录老年代region的引用情况,那么在未被引用的情况下,唯一一种不被回收的情况,就是新生代调用,而新生代的相关信息在Young GC就已经知道的,所以就可以在并发的同时进行回收。
  • 在JDK 8中,8u20之后的版本,在GC阶段,G1会把新创建的字符串放进队列。并发之后会对内部数据排重,也就是引用同一数组,可以采用以下语句激活:

-XX:+UseStringDeduplication

注意,这种排重虽然减少内存占用,但是这种并发操作会占用一些CPU资源,也会导致Young GC稍微变慢。

  • 类型卸载是在Java方面的一个难题,从ClassLoader专栏中知道,一个类只有当其自定义的ClassLoader被回收之后才会卸载,但是还是会出现问题。

旧版本G1之后再Full GC的时候才会进行类型卸载,可以用以下参数查看类型卸载:

-XX:+TraceClassUnloading

新版G1做了一定优化,8u40只有,默认开启以下选项,只在并发标记只后才卸载:

-XX:+ClassUnloadingWithConcurrentMark

  • 我们知道老年代GC要在等待并发标记之后进行对象回收。这就意味着当并发标记不足的时候,堆就会满,而GC还没有完成,就会触发Full GC,所以触发并发标记的时机很重要。
    早期的G1调优可以通过以下设置进行调整:

-XX:InitiatingHeapOccupancyPercent

新版G1,会将该参数设置为初始值,在运行的时候进行采样,动态调整并发标记启动时机,对应参数如下:

-XX:+G1UseAdaptiveIHOP

  • 在现有的资料中,大多指出G1的Full GC是最差劲的单线程串行GC,其实如果采用的是最新的JDK,你会发现Full GC也是并行运行的了,在通用场景中的表现还优于Parallel GC的Full GC实现。

调优建议

第一

尽量升级到高版本的JDK版本,如上所述,新版本JDK减少很多问题,优化很多问题。

第二

掌握GC调优信息收集途径,掌握尽量全面、详细、准确的的信息,是各种调优的基础。
良好的使用日志可以事半功倍。

连个常用的选项:

-XX:+PrintGCDetails
-XX:+PrintGCDateStamps

还有一些非常有用的日志选项,很多特定问题的诊断都是要依赖这些选项:

-XX:+PrintAdaptiveSizePolicy // 打印G1 Ergonomics相关信息

我们非常清楚,GC内部一些行为是适应性触发,利用PrintAdaptiveSizePolicy,就可以知道为什么JVM做出了一些我们不希望发生的事情。例如,G1调优的基本建议就是避免进行大量的Humongous对象分配,如果Ergonomics信息说明发生了这一点,那么就可以考虑要么增大堆的大小,要么直接将region大小提高。

如果是怀疑出现引用清理不及时的情况,则可以打开下面选项,掌握到底是哪里出现了堆积

-XX:+PrintReferenceGC

另外,建议开启选项下面的选项进行并行引用处理

-XX:+ParallelRefProcEnabled

需要注意的一点是,JDK 9中JVM和GC日志机构进行了重构,其实前面提到的PrintGCDetails已经被标记为废弃,而PrintGCDateStamps已经被移除,指定它会导致JVM无法启动,可以使用下面的命令查询新的配置参数:

java -Xlog:help

最后,看一些通用时间,理解了GC内部结构和机制,很多结论就一目了然了,例如:

  • 如果发现Young GC非常耗时,这很可能就是因为新生代太大了,我么可以考虑减少新生代的最小比例。

-XX:G1NewSizePercent
降低其最大值同样对降低Young GC延迟有帮助。
-XX:G1MaxNewSizePercent
如果我们直接为G1设置较小的延迟目标值,也会起到减小新生代的效果,虽然会影响吞吐量。

  • 如果是Mixed GC延迟较长,我们应该怎么做呢?
    部分Old region会被包含进Mixed GC,减少一次处理的region个数,就是个直接的选择之一。
    上面介绍了G1 OldCSetRegionThresholdPercent控制其最大值,还可以利用下面参数提高Mixed GC的个数,当前默认值是8,Mixed GC数量增多,意味着每次被包含的region减少。

XX:G1MixedGCCountTarget

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值