jvm stw 内存溢出相关解决方案

617 阅读3分钟

1. 背景

线上应用服务单台机器的qps达到1w+。下游调用系统对于rt比较敏感,偶尔会出现熔断。导致大面积的报警。

2.监控分析

通过监控发现有一台机器的gc时间达到3ms,导致服务出现报警。 image.png

我们先用常规的方案来分析一下线上线程以及堆栈的信息,详细如下:

##查找进程里面cpu占用最高的线程
top -Hp  pid
##top 线程
printf "%x\n" tid     
## 查看线程是否有lock or waiting 的thread                                       
jstack  pid | grep tid

## jstat 查看gc的频次
通过 jstat -gcutil pid 时间间隔 

##  jmap 导出堆栈信息
jmap -dump:format=b,file=name.hprof pid

1.jstack分析线程的信息

直接通过 gceasy heaphero heapdump 三个网站分析即可。

2.通过jmap dump的hprof文件通过mat分析

在打开MAT软件之前,需要对软件的JVM启动参数进行修改(调大软件的运行内存)(否则可能无法正常打开dump文件)

修改配置文件: [ MemoryAnalyzer.ini ]

路径:mac通过访达,点击app图像右键显示包内容.找到 MemoryAnalyzer.ini

参数: -Xms8192m -Xmx8192m 可以根据hprof文件大小确定内存大小

Snip20210920_258.png 下载mac mat分析工具,不需要安装jdk11

3.如何调优

分析发现应用程序使用的是cms垃圾回收引擎,且已经调整新生代的大小。应用程序在运行一定时间以后,老年代内存不够,触发fullgc 。fullgc的时间有3s多,需要控制fullgc的时间。经过调研把垃圾回收引擎变为g1,详细调整如下( -XX:MaxGCPauseMillis=50 控制gc的最大回收时间)。线上使用可参考相关配置,如下:

-Xmx4096m -Xms4096m -XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=512m -Xss256k -XX:+UseG1GC -XX:MaxGCPauseMillis=50 -XX:-UseLargePages -XX:+ParallelRefProcEnabled -XX:InitiatingHeapOccupancyPercent=45 -XX:+ExplicitGCInvokesConcurrent -XX:ParallelGCThreads=8 -XX:-UseBiasedLocking -XX:AutoBoxCacheMax=20000 -XX:MaxTenuringThreshold=6 [-Xloggc:/dev/shm/gc.log]() -XX:+PrintGCApplicationStoppedTime -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:+PrintCodeCache -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=2 -XX:GCLogFileSize=10m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/root/logs/

这里一定要注意:jdk官网建议设置MaxGCPauseMillis以后,就不要设置-Xmn大小。调整新生代大小通过 XX:G1NewSizePercent和-XX:G1MaxNewSizePercent两个数值指定。XX:G1NewSizePercent,默认值5%,上限:-XX:G1MaxNewSizePercent,默认值60%。G1会根据实际的GC情况(主要是暂停时间)来动态的调整新生代的大小,主要是Eden Region的个数。最好是Eden的空间大一点,毕竟Young GC的频率更大,大的Eden空间能够降低Young GC的发生次数。但是Mixed GC是伴随着Young GC一起的,如果暂停时间短,那么需要更加频繁的Young GC,同时也需要平衡好Mixed GC中新生代和老年代的Region,因为新生代的所有Region都会被回收,如果Eden很大,那么留给老年代回收空间就不多了,最后可能会导致Full GC。

4.如何分析单个对象的大小

如果应用程序的qps比较高。尽量不要创建没必要的对象,减少minor gc。如何判断单个对象的大小,可以通过引入jol包进行分析。详细如下:

<dependency>
            <groupId>org.openjdk.jol</groupId>
            <artifactId>jol-core</artifactId>
            <version>0.16</version>
 </dependency>
 
System.out.println(ClassLayout.parseClass(new xxx.getClass()).toPrintable());`
 

5.总结

线上的问题一定要积极去解决,这样才能对一些线上问题的处理有一些更深入的理解。