此文的 JVM 环境特指 HotSpot JVM
Understanding Garbage Collection
了解 JVM 内存回收机制,以及年轻代、伊甸园、年老代的意义。Full GC 发生很频繁时,关注是否分配内存不足1、死锁、或者内存泄露。
jstat - Statistics Monitoring
一系列调试选项,常用 jstat -gcutil
排查内存问题。
jstack - Stack Trace
- Generating Thread Dumps in HotSpot - 等同于
jstack [PID] > threaddump.log
- Thread.State in Java? BLOCKED vs WAITING
- Fun With JStack Blog
- Java thread state transition, WAITING to BLOCKED, or RUNNABLE?
了解不同的线程状态。通常用来排查是否有死锁发生,以及找出高 CPU 消耗的线程状态。
top -Hp [PID]
- 找出进程内最耗费CPU的线程printf "%x\n" [PID]
- 得到线程号 TIDjstack [PID] | grep [TID]
- 观察线程信息
jmap - Memory Map
分析 JVM 的内存状态,是排查内存泄露的关键。
jmap -heap [PID]
- 进程堆内存使用情况jmap -histo[:live] [PID] | head 15
- 对象占用图。对象占用图粒度很粗,无法获得更详细的对象状态jmap -dump:format=b,file=dump.hprof [PID]
- 生成 dump.hprof 文件。执行时间稍长,基于此可进行进一步的精准分析
Eclipse MAT
基于 dump.hprof 进行分析,首选主流工具 Eclipse MAT。MAT 可以开上帝视角,通过 OQL 精确定位到每个对象,用来做详细的分析挖掘很好用。不过它的缺点也很明显:重量级。如果 dump 文件不大,直接用 MAT 分析没有问题。但现实运行环境中,dump 文件通常很大2,一般需要面临以下问题:从服务器压缩传输到个人机器,压缩和传输的时间成本很高;解压缩后用 MAT 导入,如果个人机器的内存不够,无法进行解析3。
内存不够装进 Eclipse 的解决方案
找到 org.eclipse.equinox.launcher_1*.jar
的本地目录,命令行执行生成索引过程:
java -jar /Users/hedley/eclipse/java-neon/Eclipse.app/Contents/Eclipse/plugins/org.eclipse.equinox.launcher_1*.jar -consoleLog -application org.eclipse.mat.api.parse dump.hprof org.eclipse.mat.api:suspects org.eclipse.mat.api:overview org.eclipse.mat.api:top_components
生成报告,其中 org.eclipse.mat.api:suspects
、org.eclipse.mat.api:overview
、org.eclipse.mat.api:top_components
是生成的三个主要模块,可根据需要配置。再用 Eclipse 打开报告目录。
如果以上命令还报内存不足,尝试加参数 -Xmx4g -XX:-UseGCOverheadLimit
BHeapSampler
BHeapSampler 是一款轻量级的 JVM 内存分析工具。不,它不单单是轻量级,是羽毛级!如果用 MAT,通常需要把臃肿的 dump.hprof 传到本机进行操作4。而 BHeapSampler 是一个大小只有 26K 的 jar 包,上传到服务器一行指令即可进行分析。它分析得到的是一张大内存对象引用图,不能进行任何操作与细项查询索引。但是这张重点鲜明的内存图,有时会比 MAT 更击中要害。大写的赞。
java -Xmx40g -XX:-UseGCOverheadLimit -jar bheapsampler.jar dump.hprof
- 得到 memory_graph.dot- 可以用 Graphviz 查看 .dot 文件,装完也可以将 .dot 转为 pdf 格式:
/opt/local/bin/dot -Tpdf -omemory_graph.pdf memory_graph.dot