jvm内存泄漏的排查tips总结

以下是对这篇原文的总结,部分内容不够详细,请参考原文地址:https://juejin.cn/post/7255634554987020343

内存问题排查方法论

1. 问题定位流程

  • 确定进程:使用 ps aux --sort=-%mem 找出内存占用最高的进程
  • 分层排查:按照堆内 → 堆外的顺序逐步分析
  • 量化分析:通过计算得出具体的内存占用分布

2. 堆内存分析技巧

# 查看堆内存详情
jcmd <pid> GC.heap_info

# 查看真实物理内存占用
pmap -X <pid>

image

3. 堆外内存分析技巧

# 使用Arthas快速查看各内存区域
memory

# 计算公式
堆外物理内存 = 总占用物理内存 - heap占用的物理内存

jcmd GC.heap_info
image
heap:启动参数里指定了Xmx是4G,从命令结果也可以看出约等于 new generation 1.8G + old geration 2G。
Metaspace: committed 约0.1G左右
通过pmap命令可以看到最真实的JVM heap的物理内存的占有量,因为Heap本质是一个内存池,池子的总大小4G,但是实际物理内存一般达不到4G,而Heap的used也只是池子中使用部分的内存。所以还是要通过操作系统的pmap来查询:
image
如上图,JVM heap是4G的虚拟内存,启动物理内存约占用3144764K即约3G,50M左右置换到了swap空间。
所以heap实际占用约3G。

4. JNI Memory泄漏排查

方向一:gperftools工具分析,查堆栈

# 安装工具
yum install gperftools gperftools-devel graphviz ghostscript

# 设置环境变量
export LD_PRELOAD=/usr/lib64/libtcmalloc.so
export HEAPPROFILE=/home/admin/gperftools/heap/hprof

# 启动app。。。
# kill app,再运行app_start.sh

# 生成对比报告
pprof --pdf --base=heap_before.heap java heap_after.heap > mem-diff.pdf

例如:最大头的是Java_java_util_zip_Inflater_inflateBytes函数在申请堆外内存,共680M,占比680M
image

方向二:内存块分析,看内存块内容

# 查看内存块分布
(pmap -X <pid> | head -2; pmap -X <pid> | awk 'NR>2' | sort -nr -k6) > pmap.log

# dump内存块内容
gdb --batch --pid <pid> --ex 'dump memory 103.dump 0x7f8a78000000 0x7f8a7c000000'

# 查看内存数据
strings 103.dump

5. 调用栈定位技巧

# 持续打印线程堆栈
while true; do jstack <pid> | grep -A 20 -B 5 "inflateBytes"; sleep 1; done

# 或使用arthas拦截方法调用
stack java.util.zip.Inflater inflate

6. GC验证方法

# 手动触发FullGC并查看对象分布
jmap -histo:live <pid>

7. ptmalloc2内存碎片处理

手动释放内存

# 调用malloc_trim释放碎片内存
gdb --batch --pid <pid> --ex 'call malloc_trim()'

优化参数

export MALLOC_ARENA_MAX=8
export MALLOC_MMAP_THRESHOLD_=131072 
export MALLOC_TRIM_THRESHOLD_=131072 
export MALLOC_TOP_PAD_=131072 
export MALLOC_MMAP_MAX_=65536

8. 解决方案选择

推荐方案:替换内存分配器

# 使用jemalloc或tcmalloc
export LD_PRELOAD=/usr/lib64/libjemalloc.so
# 或
export LD_PRELOAD=/usr/lib64/libtcmalloc.so

业务层面优化

  • 修改Kafka压缩算法:gzip → Snappy/LZ4
  • 避免使用JVM的gzip相关JNI调用
  • 其他

9. 关键排查命令总结

ps aux --sort=-%mem          # 找出内存大户进程
jcmd <pid> GC.heap_info      # 查看堆内存
pmap -X <pid>                # 查看真实内存分布  
jstack <pid>                 # 查看线程堆栈
jmap -histo:live <pid>       # 触发GC并查看对象
gdb --batch --pid <pid> --ex 'call malloc_trim()'  # 释放碎片内存

10. 注意事项

  • malloc_trim() 有极小概率导致JVM Crash,使用需谨慎
  • 使用gperftools时,tcmalloc会间接解决问题,影响问题复现
  • 64M内存块是ptmalloc2内存碎片的典型特征
  • 不同内存分配器适用场景不同:小内存高并发用TCMalloc,通用场景用ptmalloc2
posted @ 2025-09-17 15:26  debug_me_  阅读(36)  评论(0)    收藏  举报