性能测试系列-java gc调优 (2)

G1 的很多开销都是源自 Remembered Set,例如,它通常约占用 Heap 大小的 20% 或更高,这可是非常可观的比例。并且,我们进行对象复制的时候,因为需要扫描和更改 Card Table 的信息,这个速度影响了复制的速度,进而影响暂停时间。

 在G1 算法中记录了老年代 region 间对象引用,Humongous 对象数量有限,所以能够快速的知道是否有老年代对象引用它。如果没有,能够阻止它被回收的唯一可能,就是新生代是否有对象引用了它,但这个信息是可以在 Young GC 时就知道的,所以完全可以在 Young GC 中就进行 Humongous 对象的回收,不用像其他老年代对象那样,等待并发标记结束。

8u20 以后字符串排重的特性,在垃圾收集过程中,G1 会把新创建的字符串对象放入队列中,然后在 Young GC 之后,并发地(不会 STW)将内部数据(char 数组,JDK 9 以后是 byte 数组)一致的字符串进行排重,也就是将其引用同一个数组。你可以使用下面参数激活:

-XX:+UseStringDeduplication


这种排重虽然可以节省不少内存空间,但这种并发操作会占用一些 CPU 资源,也会导致 Young GC 稍微变慢。

通过 -XX:+TraceClassUnloading
可以查看到G1 算法的类型卸载方式,8u40 以后的jdk版本中,G1 增加并默认开启下面的选项: -XX:+ClassUnloadingWithConcurrentMark

在并发标记阶段结束后,JVM 即进行类型卸载,并不会在发生了full GC才进行类型卸载。

在之前的jdk版本中,老年代对象回收,基本要等待并发标记结束。这意味着,如果并发标记结束不及时,导致堆已满,但老年代空间还没完成回收,就会触发 Full GC,所以触发并发标记的时机很重要。早期的 G1 调优中,通常会设置下面参数,但是很难给出一个普适的数值,往往要根据实际运行结果调整. -XX:InitiatingHeapOccupancyPercent


在 JDK 9 之后的 G1 实现中,这种调整需求会少很多,因为 JVM 只会将该参数作为初始值,会在运行时进行采样,获取统计数据,然后据此动态调整并发标记启动时机。对应的 JVM 参数如下,默认已经开启:


-XX:+G1UseAdaptiveIHOP
在新的jdk中,full gc已经从单线程串行 GC 变更为了并行进行了,在通用场景中的表现还优于 Parallel GC 的 Full GC 实现。

性能GC调优一些建议如下:(摘选自:Java GC调优怎么做?杨晓峰 出处 | 极客时间《Java 核心技术 36 讲》

1、首先,建议尽量升级到较新的 JDK 版本,从上面介绍的改进就可以看到,很多人们常常讨论的问题,其实升级 JDK 就可以解决了。
2、掌握 GC 调优信息收集途径。掌握尽量全面、详细、准确的信息,是各种调优的基础,不仅仅是 GC 调优。我们来看看打开 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


最后,来看一些通用实践,理解了我前面介绍的内部结构和机制,很多结论就一目了然了,例如

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

-XX:G1NewSizePercent

降低其最大值同样对降低 Young GC 延迟有帮助。

-XX:G1MaxNewSizePercent


如果我们直接为 G1 设置较小的延迟目标值,也会起到减小新生代的效果,虽然会影响吞吐量。

如果是 Mixed GC 延迟较长,我们应该怎么做呢?

还记得前面说的,部分 Old region 会被包含进 Mixed GC,减少一次处理的 region 个数,就是个直接的选择之一。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wsfjsx.html