通过 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
来调高元空间大小
接下来看一些关键的性能指标,可以看到 Avg Pause GC Time 也就是平均 GC 时间为 10 ms,最大 GC 时间为 190 ms,这些参数目前看来也正常,没有出现过长的 GC 时间
接下来看一下 GC 持续时间的一些情况,可以看到在系统刚开始就发生了几次 Full GC,这是很严重的问题,可以看到这三次 Full GC 产生的原因分别是:Metadata GC Threashold
和 Ergonomics
,即元空间超过阈值,Ergonomics
的含义是自动调节 GC 暂停时间和吞吐量从而产生的 GC,是虚拟机中对性能的优化,那么因为 Ergonomics
产生的 GC 我们可以不管,总结一下这几次 Full GC 产生的原因就是 元空间超过阈值!
最后我们可以看一下 GC 的指标,可以看到 Full GC 总共发生了 6 次,还是比较多的,需要控制一下 Full GC 的次数,因为 Full GC 对系统性能影响是比较大的
上边我们已经通过 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 内存的使用更加合理了,新生代也没有超过分配的内存大小,如下图:
并且 Full GC 的次数为 0
这里需要注意的是,如果使用 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 步:
- 开启 G1 垃圾收集器
- 设置堆内存
- 设置最大的停顿时间
# 设置堆、元空间大小
-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