Arthas排查Java堆内存溢出

1. 启动Arthas并附加到目标JVM

# 安装Arthas(如果未安装)
curl -O https://arthas.aliyun.com/arthas-boot.jar
java -jar arthas-boot.jar

# 选择目标Java进程的PID(输入数字)
[INFO] arthas-boot version: 3.7.1
[INFO] Found existing java process, please choose one and input the serial number of the process, eg: 1.
1: 1234 org.example.Main
2: 5678 another.app.Main

2. 实时监控内存和GC状态

2.1 使用dashboard命令

查看实时内存、GC、线程状态:

dashboard
  • 观察指标

    • heap内存使用是否持续增长(接近max值)。

    • GC频率是否异常(频繁Full GC可能表明内存不足)。

2.2 使用jvm命令

确认堆内存配置:

jvm
  • 检查MaxHeapSize(即-Xmx参数)是否合理。若配置过小,需调整JVM参数。


3. 生成Heap Dump分析

3.1 使用heapdump命令导出堆快照
heapdump --live /tmp/heapdump.hprof
  • --live:仅导出存活对象(减少文件大小)。

  • 文件路径需有写入权限,推荐使用/tmp或自定义目录。

3.2 使用工具分析Heap Dump
  • heapdump.hprof下载到本地,用以下工具分析:

    • Eclipse MAT:查找内存泄漏(Dominator Tree、Leak Suspects)。

    • JVisualVM:直观查看大对象分布。


4. 在线分析内存对象

4.1 使用memory命令查看内存占用

列出堆内存中对象实例数和内存占用:

memory

或按类名排序:

memory | grep 'java.lang.'  # 过滤特定包名
4.2 使用vmtool获取类实例详情

查看某个类的实例数量和属性:

vmtool --action getInstances --className java.util.ArrayList --express 'instances.length'

5. 检查线程和代码逻辑

5.1 使用thread命令查看线程栈
thread -n 5  # 查看最忙的5个线程
  • 检查是否有线程在大量分配对象(如循环中创建大集合)。

5.2 使用tracewatch跟踪方法

跟踪可能分配内存的方法:

# 监控某个方法耗时和调用栈
trace com.example.MyService processData

# 查看方法参数/返回值中的大对象
watch com.example.MyService processData '{params, returnObj}' -x 3

6. 动态排查内存泄漏

6.1 使用ognl表达式检查对象增长

多次执行以下命令,观察对象数量是否持续增长:

ognl '@java.lang.management.ManagementFactory@getMemoryMXBean().getHeapMemoryUsage().getUsed()'
6.2 使用tt记录方法调用

记录方法调用,分析参数是否异常:

tt -t com.example.MyService addData -n 100
tt -i 1000 -w '{target,params[0]}'  # 查看第1000次调用的参数

7. 确认JVM参数优化

  • 如果确认是配置问题,调整JVM参数:

    -Xmx4g -Xms4g -XX:+UseG1GC -XX:+HeapDumpOnOutOfMemoryError
  • -XX:+HeapDumpOnOutOfMemoryError:OOM时自动生成堆快照。


常见原因总结

  1. 内存泄漏:对象因代码逻辑(如静态集合、未关闭资源)无法回收。

  2. 配置不足-Xmx设置过小,无法承载业务负载。

  3. 大对象分配:一次性加载超大文件或缓存。

  4. GC效率低:老年代碎片化或GC算法不匹配。


通过以上步骤,可以快速定位是配置问题、代码缺陷还是资源管理不当导致的OOM。