JVM虚拟机系统性学习-JVM调优之通过gceasy分析GC日志对堆、元空间、线程堆栈和垃圾回收器进行调优

通过 gceasy工具对生成的 GC 日志进行分析

这里使用的 JDK 版本为 JDK8!

在分析 GC 日志时,可以同时采用多种工具(Arthas、gceasy、JVM 连接 Graphana 监控)进行分析,避免某种工具分析不准确

gceasy 每个月只可以免费分析 5 个 gc 日志,因此要节约机会!hhh!

我们先将 gc.log 文件放入 gceasy 中进行分析,分析结果如下:

首先是 JVM 内存大小,可以看到新生代分配了 624 mb,而 Peak 也就是峰值也达到了 624 mb,说明新生代很容易就被占满了,而对于元空间 Meta Space 来说,分配了 1 个 gb,而峰值才使用了 59 mb,因此元空间分配的大小也不合理,对于 JDK8 来说,如果不指定元空间的大小,默认元空间的最大值是系统内存的大小,在 64 位操作系统中,元空间默认初始值为 21MB,如果初始未给定的元空间的大小,导致初始元空间过小,会 频繁触发 Full GC 来调高元空间大小

JVM虚拟机系统性学习-JVM调优之通过gceasy分析GC日志对堆、元空间、线程堆栈和垃圾回收器进行调优_第1张图片

接下来看一些关键的性能指标,可以看到 Avg Pause GC Time 也就是平均 GC 时间为 10 ms,最大 GC 时间为 190 ms,这些参数目前看来也正常,没有出现过长的 GC 时间

JVM虚拟机系统性学习-JVM调优之通过gceasy分析GC日志对堆、元空间、线程堆栈和垃圾回收器进行调优_第2张图片

接下来看一下 GC 持续时间的一些情况,可以看到在系统刚开始就发生了几次 Full GC,这是很严重的问题,可以看到这三次 Full GC 产生的原因分别是:Metadata GC ThreasholdErgonomics,即元空间超过阈值,Ergonomics 的含义是自动调节 GC 暂停时间和吞吐量从而产生的 GC,是虚拟机中对性能的优化,那么因为 Ergonomics 产生的 GC 我们可以不管,总结一下这几次 Full GC 产生的原因就是 元空间超过阈值!

JVM虚拟机系统性学习-JVM调优之通过gceasy分析GC日志对堆、元空间、线程堆栈和垃圾回收器进行调优_第3张图片

最后我们可以看一下 GC 的指标,可以看到 Full GC 总共发生了 6 次,还是比较多的,需要控制一下 Full GC 的次数,因为 Full GC 对系统性能影响是比较大的

JVM虚拟机系统性学习-JVM调优之通过gceasy分析GC日志对堆、元空间、线程堆栈和垃圾回收器进行调优_第4张图片

上边我们已经通过 gceasy 分析了 gc 日志了,存在的问题主要有以下几点:

  • Meta Space 空间分配不合理
  • Full GC 产生次数过多
堆和元空间优化

那么优化参数我们从 堆空间元空间新生代 3 个方面进行入手,参数调整如下:

-Xms1096m -Xmx1096m -Xmn408m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=128m
  • 堆空间通过 -Xms -Xmx 来进行调整,为了尽量避免 Full GC,堆空间可以设置为 Full GC 后老年代空间占用的 3-4 倍 ,这样的话一般可以避免老年代空间不足从而导致 Full GC 的情况,最好设置为 8 的整数倍,我们通过上边 easygc 分析中的 JVM Memory Size 得知,老年代的峰值为 274mb,因此这里设置堆空间大小为 274 * 4 = 1096 mb,设置堆空间为 Full GC 后老年代对象的 4 倍大小
  • 元空间通过 -XX:MetaspaceSize=N 来设置,这里设置元空间大小为 128 mb
  • 新生代通过 -Xmn 来设置,新生代可以设置为 Full GC 后老年代空间占用的 1-1.5 倍 ,即 274 * 1.5 = 411 mb,最好设置为 8 的整数倍,因此改为 408 mb

可以看到优化后,JVM 内存的使用更加合理了,新生代也没有超过分配的内存大小,如下图:

JVM虚拟机系统性学习-JVM调优之通过gceasy分析GC日志对堆、元空间、线程堆栈和垃圾回收器进行调优_第5张图片

并且 Full GC 的次数为 0

JVM虚拟机系统性学习-JVM调优之通过gceasy分析GC日志对堆、元空间、线程堆栈和垃圾回收器进行调优_第6张图片

这里需要注意的是,如果使用 Docker 部署的 java 应用,可以在 Dockerfile 中设置 JVM 的参数,并且在启动的时候,尽量去将 JVM 参数打印出来,确保设置的参数生效!

线程堆栈优化

上边对 JVM 中的堆和方法区的大小进行了优化,接下来看一下如何对 JVM 中的线程堆栈进行优化

JDK5.0 后每个线程堆栈大小为 1M,在相同物理内存下,线程堆栈越小,就能生成更多的线程,但是操作系统对一个进程内的线程数量还是有限制的,如果堆栈不是很深可以设置 256k,如果是很大的应用可以使用 512k

对于平常的系统来说,是不需要进行线程堆栈的优化的,但是如果开发一些中间件的话,需要创建出很多的线程,那么对于线程堆栈的优化还是比较有必要的,线程堆栈大小设置通过 -Xss 进行设置

-Xms1096m -Xmx1096m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=128m -Xss512k
垃圾回收器组合优化
  • 吞吐量优先:Parallel Scavenge + ParallelOldGC
  • 响应时间优先(低延迟):ParNew + CMS
G1 垃圾回收器

G1 兼顾了吞吐量和响应时间,尤其在大内存的情况下比较好,配置 G1 只需要 3 步:

  1. 开启 G1 垃圾收集器
  2. 设置堆内存
  3. 设置最大的停顿时间
 # 设置堆、元空间大小
 -Xms256m -Xmx256m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=128m -Xss512k
 # 开启 G1
 -XX:+UseG1GC -XX:MaxGCPauseMillis=100
  # 开启 GC 日志创建更详细的 GC 日志
 -XX:+PrintGCDetails -XX:+PrintGCTimeStamps,-XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -Xloggc:./logs/gc.log

你可能感兴趣的:(JVM,jvm,学习)