记录一次线上服务OOM
MAT
-
下载
- https://archive.eclipse.org/mat/
- 选择对应平台以及对应系统架构进行下载。
- 启动的时候记得修改MemoryAnalyzer.ini中堆内存默认是1g修改成大于要分析的dump文件大小。
- 注意事项 看看自己服务器jdk版本。
- 需要工具可以评论私发给你们
Memory Analyzer 1.14 and later needs a Java 17 VM or later VM to run. Memory Analyzer 1.12 and later needs a Java 11 VM or later VM to run. The Memory Analyzer 1.8 to 1.11 needs a Java 1.8 VM or later VM to run (of course, heap dumps from JDK 1.4.2_12 on are supported). If in doubt, provide the runtime VM on the command line: MemoryAnalyzer.exe -vm path/to/java17/bin 翻译: Memory Analyzer 1.14 及更高版本需要 Java 17 VM 或更高版本的虚拟机才能运行。Memory Analyzer 1.12 及更高版本需要 Java 11 VM 或更高版本的 VM 才能运行。Memory Analyzer 1.8 至 1.11 需要 Java 1.8 VM 或更高版本的 VM 才能运行(当然,也支持 JDK 1.4.2_12 开始的堆转储)。如有疑问,请在命令行中提供运行时虚拟机:
-
基本概念
- Heap Dump
是java进程堆内存在一个时间点的快照。支持HPROF及DTFJ格式。前者是有Oracle系列JVM生成 后者IBM系列JVM生成。- 所有对象的实例信息: 对象所属类名,基础类型和引用类型的属性等。
- 所有类信息: 类加载器,类名,继承关系,静态属性等。
- GC Root: GC Root 代表通过可达性分析来判定JVM对象是否存活的起始集合。
- 线程栈及局部变量:快照生成时刻的所有线程的线程栈帧
- Shallow Headp
- 代表一个对象结构自身所占用的内存大小,不包括其属性引用对象所占的内存。
- Retained Set
- 一个对象的 Retained Set,指的是该对象被 GC 回收后,所有能被回收的对象集合。
- Retained Heap
- 是一个对象被 GC 回收后,可释放的内存大小,等于释放对象的 Retained Heap 中所有对象的 Shallow Heap 的和。
- Dominator tree
- 如果所有指向对象 Y 的路径都经过对象 X,则 X 支配(dominate) Y(
- OQL
- 是类似于 SQL 的 MAT 专用统一查询语言,可以根据复杂的查询条件对 dump 文件中的类或者对象等数据进行查询筛选
- references
- outgoing references:对象引用的外部对象。
- incoming references:直接引用了当前对象的对象。
- Heap Dump
arthas(备用)
- 离线版本arthas
https://github.com/alibaba/arthas/releases/download/arthas-all-3.6.4/arthas-bin.zip
2. 执行 ./install-local.sh之后正常使用 此工具也可以分析栈空间以及堆占用
3. 保证跟查询服务启动用户一致
问题分析
- 堆内存比较大 生产上不能copy出来
- 生产arm架构 下载对应版本linux工具 MemoryAnalyzer-1.13.0.20220615-linux.gtk.aarch64.zip 使用jdk11
- 执行脚本
./ParseHeapDump.sh [hprof文件] org.eclipse.mat.api:suspects org.eclipse.mat.api:overview org.eclipse.mat.api:top_components
- 执行结果
- 执行结果分析
- ThreadPoolExecutor LinkedBlockingQueue$Node 对象占据了11g 分析得出 此线程池处理速度慢 任务堆积;
- 查看代码同事使用的是jdk自带工具包队列
此线程池队列内部是使用的LinkedBlockingQueue队列大小Integer.MAX_VALUE无限大。
3.想到的方法是使用自定义线程池指定队列大小,采用拒绝策略使用负反馈机制,避免无限提交任务。(临时解决)
4.进一步分析问题,为啥线程处理慢 导致任务堆积。查询日志发现消费过程中查询数据库sql耗时。
5.分析耗时原因从sql是否命中索引以及数据库连接数排查。异步并发任务太多
6.排查代码中异步并发任务,根据业务出发减少不必要的并发循环代码。同时增大数据库连接数 测试和线上均无复现该任务
SHOW STATUS LIKE 'Threads_connected'; 查询数据库线程连接数
问题解决方案以及监控
-
java 启动参数说明
/usr/lib/jvm/jdk-1.8-oracle-aarch64/bin/java -Djava.io.tmpdir=./temp -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=19999 -jar aicc-yyfx-server-2.0.1.zszq.jar -Xms4g -Xmx6g -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/home/hcicloud_9.7.2_zhaoshangzq/aicc-yyfx-servr/aicc-yyfx-zszq.hprof -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCCause -XX:+UseGCLogFileRotation -XX:+PrintHeapAtGC -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=20M -Xloggc:/home/hcicloud_9.7.2_zhaoshangzq/aicc-yyfx-server/log/aicc-yyfx-zszq-gc-%t.log
-
-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=19999
- 远程调试使用
-
-XX:+ExitOnOutOfMemoryError
- 如果出现了 oom,就会自动退出程序,咱们的健康检测自然能发现应用不存在了,从而能发出告警 不至于让程序处于混沌状态。对外无响应。
-
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/home/hcicloud_9.7.2_zhaoshangzq/aicc-yyfx-servr/aicc-yyfx-zszq.hprof
- 出现OOM之后可以对服务滚动停机新增参数后再次重启。复现问题进行排查。(出现问题时临时开启不建议在生产环境中默认开启这些参数,以避免不必要的性能开销和磁盘空间占用)
-
XX:+HeapDumpBeforeFullGC
- 参数会在每次 Full GC 之前生成一个堆转储文件,并保留不可达对象 (排查频繁fullgc的时候使用)(出现问题时临时开启不建议在生产环境中默认开启这些参数,以避免不必要的性能开销和磁盘空间占用)
-
生产环境中开启 GC 日志,以便监控 GC 行为。这些参数的性能开销较小 建议开启
-Xloggc:/home/hcicloud_9.7.2_zhaoshangzq/aicc-yyfx-server/log/aicc-yyfx-zszq-gc-%t.log 设置日志目录和日志名称 -XX:+UseGCLogFileRotation 开启滚动生成日志 -XX:NumberOfGCLogFiles=5 滚动GC日志文件数,默认0,不滚动 -XX:GCLogFileSize=20M GC文件滚动大小,需开启UseGCLogFileRotation -XX:+PrintGCDetails 开启记录GC日志详细信息(包括GC类型、各个操作使用的时间),并且在程序运行结束打印出JVM的内存占用情况 -XX:+ PrintGCDateStamps 记录系统的GC时间 -XX:+PrintGCCause 产生GC的原因(默认开启)
-
常见的oom错误
java.lang.OutOfMemoryError: Java heap space java.lang.OutOfMemoryError: unable to create new native thread (无法创建更多的操作系统线程,) java.lang.OutOfMemoryError: Metaspace (修改 -XX:MaxMetaspaceSize 启动参数,调大永久代空间即可) java.lang.OutOfMemoryError: Direct buffer memory (默认大小为 64 MB,一旦使用超出限) java.lang.OutOfMemoryError: GC overhead limit (当 jvm 98%的时间都在 GC 时,就会出现该情况)
-
可以通过日志监控 java.lang.OutOfMemoryError,就可以知道应用是否出现oom.
-
openjdk 下载地址
https://adoptium.net/es/