问题

测试环境的某台机器运行一段时间后发生了oom,类型是heap oom,即堆内存溢出,用不了了,然后由于配置了-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/data/logs/,在发生oom后,会自动生成dump文件,所以可以拿着dump进行分析,进程先重启恢复使用。

前置信息

java进程的JVM参数如下,堆内存2g,其中老年代1g,新生代1g

-Xmx2048M -Xms2048M -Xmn1024M 
-XX:MaxMetaspaceSize=512M -XX:MetaspaceSize=512M 
-XX:+UseConcMarkSweepGC -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70 -XX:+ExplicitGCInvokesConcurrentAndUnloadsClasses -XX:+CMSClassUnloadingEnabled -XX:+ParallelRefProcEnabled -XX:+CMSScavengeBeforeRemark 
-XX:+HeapDumpOnOutOfMemoryError -XX:ErrorFile=/data/logs/hs_err_pid%p.log -Xloggc:/dev/shm/gc.log -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=2 -XX:GCLogFileSize=16M 
-XX:HeapDumpPath=/data/logs/ -XX:+PrintGCDetails -XX:+PrintGCDateStamps 
-XX:+PrintClassHistogramBeforeFullGC -XX:+PrintClassHistogramAfterFullGC 
-XX:+PrintCommandLineFlags -XX:+PrintGCApplicationConcurrentTime 
-XX:+PrintGCApplicationStoppedTime -XX:+PrintTenuringDistribution -XX:+PrintHeapAtGC

排查过程

MAT

使用mat分析dump文件

选择leak suspect,检测下内存泄露

image-20230914171847827

Overview

然后会给一个基本的报告,这个报告就比较明显了,dump文件1.9个g,其中1.8g是problem suspect1,而下面problem suspect1全是

java.lang.Thread对象,413个线程占了1.8g内存,平均每个线程占用了4m的内存,看线程名称的话,初步分析是rocketmq消费的时候出了问题

image-20230914172320532

histogram

直方图以类为维度,展示了类有多少个实例,shallow heap和retained heap的内存大小

shallow heap翻译过来是浅堆,就是类对象自身占用的内存,retained heap翻译过来就是深堆,就是类对象本身以及对象的成员变量占用的内存

看如下的直方图,可以看到2g的对,排名第一的java.lang.StackTraceElement这个类,对象数是29344740个,2900万个,然后shallow heap和retained heap大概是895m,占了一半的堆内存,问题可能就出现在这了。继续往下看

image-20230915084604031

dominator tree

下一步就是看一下支配树这一项了,会按照内存占用比例从高到低展示对象排序。

支配树内存占用排名前32位全都是java.lang.Thread,每个线程内存占用量按照55m算,55*32=1760m,大概占了堆的85%+,和overview给出的数据基本一致

这32个线程都是rocketmq的消费线程,那应该是消费出问题了,继续点开线程查看详情

image-20230915085322575

展开第一个线程详情之后,可以看到具体的内存占用来源了,thread_local持有的map占用了所有的内存,层层展开之后,可以看到每个key只有0.01m内存占用,但是数量比较多,一共有3874个entry,折合下来38m,差不多都是org.mybatis.spring.MyBatisSystemException这种异常造成的了

image-20230915090508790

复制下cause信息看下

Data too long for column 'name' at row 1

堆栈信息如下,其他的线程也是类似,那可以得到如下结论:mq消费的时候,由于mybatis执行更新发生异常,cat的context持有了异常信息,异常过多导致了oom

image-20230915092353441

小结

本次的oom原因比较直观,基本上看下overview,histogram,dominator tree就可以得出结论了

修复也比较简单,要么限制下入库时name字段的长度,要么将name字段的长度调大一点就好了