线上堆 12G,jmap 导出用了 90 秒,MAT 打开时提示需要 8G 以上堆,加载二十分钟还没算完,只想找哪个对象把老年代撑住。第一次遇到会往复杂处想,其实漏掉的多是个小前提。
jcmd 21455 GC.class_histogram | head -20 jmap -dump:live,format=b,file=/tmp/live.hprof 21455 ./MemoryAnalyzer -vmargs -Xmx10g
全量 dump 包含了所有线程栈和重复的 char[],MAT 默认要建索引。直接看 histogram 只能看到类占比,定位不到具体是哪一次请求留下的引用。这一点先确认清楚,后面就不用反复试。
「堆转储引用链」相关的验证我通常做两遍,改完一次、隔天再看一次,确认不是临时生效。把这次用到的命令存进笔记,比记住结论有用,换个环境还能照着走。
改完隔一天再看一次,当场正常不代表能稳住。测试环境和线上的差别,往往就在版本号和那几行被忽略的配置上。
具体做法是,先用 jcmd GC.class_histogram 找占比最高的两三个类;再用 jmap -dump:live 只导存活对象 体积能小一半;MAT 里先看 dominator tree 按 retained heap 排序;对最大对象右键 Path to GC Roots 排除弱引用。
改完照着核一遍,能在十分钟内打开 dump 并定位到一个具体类的实例数超过十万。
