
1. 如何快速定位内存泄漏?定位内存泄漏一般可以分三步走。1.1 确认是否真的泄露最简单的方式是通过如下指令确认jstat -gcutil pid 1000接下来让我分析一下指令含义首先是jstat它是JDK自带工具不在系统自带命令集必须满足两点a. 服务器/本机安装了JDK不是只装JRE配置好环境变量b. 命令执行终端和Java进程在同一操作系统的用户下root进程普通用户看不到会报找不到进程然后就是配套参数gcutil打印GC统计百分比使用率、回收次数、耗时最常用的GC监控参数pid目标Java应用进程 ID1000采样间隔单位毫秒每 1000ms1 秒输出一行数据无末尾数字 只输出1次加数字 持续滚动输出。直观效果终端会每秒刷新一行GC指标持续打印直到手动按CtrlC终止命令。 用于实时观察堆内存占用、GC 频率、STW 耗时排查内存泄漏、频繁 FullGC、OOM 问题。标准输出样例S0 S1 E O M CCS YGC YGCT FGC FGCT GCT 0.00 22.35 45.21 18.64 92.10 88.32 123 1.456 2 0.872 2.328字段解释字段含义S0Survivor0 区内存使用率 %S1Survivor1 区内存使用率 %EEden 新生代使用率 %OOld 老年代使用率 %MMetaspace 元空间使用率 %CCS压缩类空间使用率YGC年轻代 GCMinor GC总次数YGCT所有 Minor GC 总耗时秒FGCFull GC 总次数FGCT所有 Full GC 总耗时秒GCT程序启动以来 GC 总耗时观察GC情况如果老年代内存使用率一直涨执行Full GC后也降不下来那基本就是有内存泄漏了。或者看监控曲线内存一直往上走呈现锯齿状但低点越来越高这就是典型特征。1.2 通过dump堆快照导出对象指令如下jmap -dump:live,formatb,fileheap.hprof pid接下来让我分析一下指令含义首先是jmap它是JDK自带堆快照工具用于导出Java进程堆内存文件以便离线分析内存泄漏、大对象、OOM。它的使用条件同jstat一致。-dump代表执行堆转储dump堆快照后面跟多个配置参数用逗号分隔。参数逐项解析live只导出当前存活对象它会先触发一次FullGC把已经没有引用的垃圾对象过滤掉快照文件更小、分析更干净。如果去掉live就会导出全量堆包含已死亡未回收对象文件更大。formatb固定写法b binary二进制格式标准hprof二进制文件是MAT、JProfiler、IDEA内存分析工具通用格式不可省略。fileheap.hprof指定导出的文件名heap.hprof文件生成在当前执行命令的目录可自定义路径例如file/tmp/heap_20260704.hprof。pid目标 Java 进程号可以用“ jps -l ”或者“ ps -ef | grep java ”获取。执行后效果a. 程序会暂停STWStop-The-World堆越大停顿越久线上业务高峰期慎用b. 磁盘生成.hprof二进制堆文件c. 导出完成后终端输出完成提示Java进程恢复正常运行。如果担心OOM突然发生来不及dump可以提前加JVM参数-XX:HeapDumpOnOutOfMemoryError这样OOM时会自动dump。1.3 用MAT分析也可选择其他内存分析工具用Eclipse Memory Analyzer打开.hprof文件后它会自动给一个Leak Suspects报告通常能直接定位问题。如果报告不够明确可以看Histogram视图找到实例数量特别多或者占用内存特别大的对象然后右键选择Path to GC Roots选exclude weak references这样就能看到是哪些强引用持有了这个对象导致它回收不了。比如说你可能看到某个静态HashMap持有了这些对象或者被ThreadLocal持有问题就很清楚了。1.4 总结还有个实用技巧是对比两次堆快照。间隔一段时间dump两次用MAT的Compare功能对比增长最多的对象往往就是泄漏的对象这对缓慢泄漏的问题特别有效。如果是本地环境调试可以直接用JProfiler实时监控能看到内存实时分配情况和对象引用关系比dump快照更直观。基本上这套流程下来大部分内存泄漏都能定位到。关键就是先确认有泄漏然后dump堆快照最后用MAT分析GC Roots引用链找到是哪里的代码持有了不该持有的引用。大多数遇到的内存泄漏问题十有八九都是静态集合没清理、ThreadLocal没remove或者监听器没反注册这几种情况看到引用链基本就能马上判断出来。