记录一次线上服务OOM

MAT

  1. 下载

    1. https://archive.eclipse.org/mat/
    2. 选择对应平台以及对应系统架构进行下载。
    3. 启动的时候记得修改MemoryAnalyzer.ini中堆内存默认是1g修改成大于要分析的dump文件大小。
    4. 注意事项 看看自己服务器jdk版本。
    5. 需要工具可以评论私发给你们
     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 开始的堆转储)。如有疑问,请在命令行中提供运行时虚拟机:
    
  2. 基本概念

    1. Heap Dump
      是java进程堆内存在一个时间点的快照。支持HPROF及DTFJ格式。前者是有Oracle系列JVM生成 后者IBM系列JVM生成。
      • 所有对象的实例信息: 对象所属类名,基础类型和引用类型的属性等。
      • 所有类信息: 类加载器,类名,继承关系,静态属性等。
      • GC Root: GC Root 代表通过可达性分析来判定JVM对象是否存活的起始集合。
      • 线程栈及局部变量:快照生成时刻的所有线程的线程栈帧
    2. Shallow Headp
    • 代表一个对象结构自身所占用的内存大小,不包括其属性引用对象所占的内存。
    1. Retained Set
      • 一个对象的 Retained Set,指的是该对象被 GC 回收后,所有能被回收的对象集合。
    2. Retained Heap
      • 是一个对象被 GC 回收后,可释放的内存大小,等于释放对象的 Retained Heap 中所有对象的 Shallow Heap 的和。
    3. Dominator tree
      • 如果所有指向对象 Y 的路径都经过对象 X,则 X 支配(dominate) Y(
    4. OQL
      • 是类似于 SQL 的 MAT 专用统一查询语言,可以根据复杂的查询条件对 dump 文件中的类或者对象等数据进行查询筛选
    5. references
      • outgoing references:对象引用的外部对象。
      • incoming references:直接引用了当前对象的对象。

arthas(备用)

  1. 离线版本arthas
    https://github.com/alibaba/arthas/releases/download/arthas-all-3.6.4/arthas-bin.zip
    2. 执行 ./install-local.sh之后正常使用 此工具也可以分析栈空间以及堆占用
    3. 保证跟查询服务启动用户一致

问题分析

  1. 堆内存比较大 生产上不能copy出来
  2. 生产arm架构 下载对应版本linux工具 MemoryAnalyzer-1.13.0.20220615-linux.gtk.aarch64.zip 使用jdk11
  3. 执行脚本
    ./ParseHeapDump.sh [hprof文件]  org.eclipse.mat.api:suspects org.eclipse.mat.api:overview org.eclipse.mat.api:top_components
    
  4. 执行结果
    1733382265699
    1733382520922
    1733382496775
  5. 执行结果分析
    1. ThreadPoolExecutor LinkedBlockingQueue$Node 对象占据了11g 分析得出 此线程池处理速度慢 任务堆积;
    2. 查看代码同事使用的是jdk自带工具包队列
      1733382656917
      此线程池队列内部是使用的LinkedBlockingQueue队列大小Integer.MAX_VALUE无限大。
      3.想到的方法是使用自定义线程池指定队列大小,采用拒绝策略使用负反馈机制,避免无限提交任务。(临时解决)
      1733383000054
      4.进一步分析问题,为啥线程处理慢 导致任务堆积。查询日志发现消费过程中查询数据库sql耗时。
      1733383295588
      1733383300185
      1733383304679
      5.分析耗时原因从sql是否命中索引以及数据库连接数排查。异步并发任务太多
      1733383389269
      6.排查代码中异步并发任务,根据业务出发减少不必要的并发循环代码。同时增大数据库连接数 测试和线上均无复现该任务
      SHOW STATUS LIKE 'Threads_connected'; 查询数据库线程连接数

问题解决方案以及监控

  1. 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
    
  2. -Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=19999

    • 远程调试使用
  3. -XX:+ExitOnOutOfMemoryError

    • 如果出现了 oom,就会自动退出程序,咱们的健康检测自然能发现应用不存在了,从而能发出告警 不至于让程序处于混沌状态。对外无响应。
  4. -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/home/hcicloud_9.7.2_zhaoshangzq/aicc-yyfx-servr/aicc-yyfx-zszq.hprof

    • 出现OOM之后可以对服务滚动停机新增参数后再次重启。复现问题进行排查。(出现问题时临时开启不建议在生产环境中默认开启这些参数,以避免不必要的性能开销和磁盘空间占用)
  5. XX:+HeapDumpBeforeFullGC

    • 参数会在每次 Full GC 之前生成一个堆转储文件,并保留不可达对象 (排查频繁fullgc的时候使用)(出现问题时临时开启不建议在生产环境中默认开启这些参数,以避免不必要的性能开销和磁盘空间占用)
  6. 生产环境中开启 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的原因(默认开启)
    
  7. 常见的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 时,就会出现该情况)
    
  8. 可以通过日志监控 java.lang.OutOfMemoryError,就可以知道应用是否出现oom.

  9. openjdk 下载地址
    https://adoptium.net/es/

posted @ 2024-12-05 15:26  贺艳峰  阅读(29)  评论(3编辑  收藏  举报