☰
MemoryAnalyzer启动闪退与heap dump OOM配置排查
2026/11/4 5:28:10 网站建设 项目流程

简介:MemoryAnalyzer(MAT)1.6.1 Windows 64位版是一款面向Java开发、测试与运维人员的内存分析工具,主要用于读取Java堆转储文件,排查内存泄漏、对象异常增长和垃圾回收受阻等问题。压缩包共463个文件,约57.99MB,以jar运行库、xml与properties配置、html帮助文档及png/gif界面资源为主,并配有可执行入口和说明文档,解压后可在本地图形界面中开展堆分析。已有303人学习下载。工具提供支配树、泄漏嫌疑报告、直方图、引用链追踪等视图,能够帮助读者快速定位占用内存较大的对象及其引用路径,理解泄漏成因,并据此优化代码与JVM参数。对于需要处理线上堆转储、复盘内存问题或建立排查思路的开发者,这份完整分发包具备直接可用的参考价值。

1. 双击 MemoryAnalyzer.exe 闪退,问题往往不在包本身

线上接口隔几小时 OOM 一次,抓下来的 heap dump 有 3.8G。我下的是 Memory Analyzer-1.6.1.20161125-win32.win32.x86_64.zip,解压双击 MemoryAnalyzer.exe,界面闪一下就退;换台机器能进主界面,打开 dump 又弹 Out of memory。折腾到最后发现包是好的,是 ini 里 -Xmx 还停在默认的 1024m。这套组合在排查现场出现频率极高:MAT 1.6.1 长期跑在 Java 8 上,解压即用、没有安装程序,正因为它够「绿色」,JVM 位数、Java 版本、解压路径、堆参数全得自己定死,任何一项偏了,表现都是「打不开」。下面按我自己装机的顺序走一遍,从环境核对、ini 改写,一直到最后拿 dump 验证跑通。

2. 装前把环境对上:MAT 1.6.1 要的 64 位 JDK 8 与目录规矩

2.1 从文件名读出三个硬条件

先把包名拆开看。MemoryAnalyzer-1.6.1.20161125-win32.win32.x86_64.zip里,1.6.1是版本,20161125是构建日期,win32.win32.x86_64是 Eclipse 平台的目标平台三元组:第一个win32指操作系统,第二个win32指窗口系统,x86_64指 CPU 架构。也就是说,这个包只能在 64 位 Windows 上跑,而且必须由一个 64 位的 JVM 带起来。32 位系统或者只有 32 位 JRE 的机器,必须换win32.win32.x86那个包,两者不能混用,混用的结果就是启动器直接退出,连主界面都看不到。

第二个硬条件是 Java 版本。MAT 1.6.x 基于 2016 年前后的 Eclipse 平台,官方支持线停在 Java 8。机器上装没装 Java 17、21 都无所谓,但给 MAT 用的那个 JVM 必须是 8。我见过不少人在一台已经装了新 JDK 的机器上直接双击,启动器去找 PATH 里第一个 java,拿到 17,插件加载阶段就崩,日志里一堆类找不到。常见做法是专门留一个 JDK 8 目录,只在 ini 里指向它,不往系统 PATH 里塞。

第三个硬条件是路径。解压目标不要放在C:\Program Files、C:\Program Files (x86)这类带空格又受 UAC 管制的目录,也不要放在中文路径下。原因是 MAT 打开 dump 时会在 dump 所在目录和自身目录写索引文件,路径里带空格在 Windows 上要靠命令行引号兜住,launcher 传参时容易丢;中文路径还可能触发编码问题,报错信息又极其含糊。我一般固定放在D:\tools\mat161这种全英文短路径下。

磁盘空间要单独算一笔账。dump 文件本身是一份,解析时生成的索引文件(.index、.domTree、.a2s之类)通常是 dump 体积的 30% 到 60%,再加上操作系统的临时文件,我一般按 dump 大小的 3 倍预留。一份 4G 的 dump 放在只剩 5G 的分区上,解析到一半磁盘写满,前面的时间全白费。

物理内存也要和后面的-Xmx配套。-Xmx给 8g,机器至少得有 12G 以上,否则 MAT 自己没崩,系统先开始疯狂换页,整个桌面卡成幻灯片。

项目要求我这套的取值不满足时的典型表现
操作系统64 位 WindowsWindows 10 专业版 64 位双击后进程一闪而过
JVMJDK 8,64 位jdk1.8.0_202 x64报 Exit code=13 或插件加载失败
解压路径全英文、无空格、非系统盘D:\tools\mat161索引写不进去,解析中断
可用磁盘dump 大小的 3 倍200G 空闲解析进度到中途报错
物理内存≥ -Xmx + 4G32G机器整体卡死,鼠标都拖不动

2.2 装 JDK 8 并当场确认位数

JDK 8 的获取方式各环境不一样,内网仓库、离线安装包、已有的老项目环境都行,原则只有一条:选 x64 的那份。装完不要急着配JAVA_HOME,先确认这个 java 到底是什么位数、什么版本,因为 PATH 上很可能有别的 JDK 抢在前面。

# 先看目录下有哪些 JDK,确认 8 的那份在哪 Get-ChildItem 'C:\Java' -Directory | Select-Object -ExpandProperty Name # 用绝对路径调用,绕开 PATH 上的其他 java & 'C:\Java\jdk1.8.0_202\bin\java.exe' -version # 更直接地看数据模型,64 就是 64 位 & 'C:\Java\jdk1.8.0_202\bin\java.exe' -XshowSettings:properties -version 2>&1 | Select-String 'sun.arch.data.model'

-version会输出三行,第三行末尾写着64-Bit Server VM就说明是 64 位;-XshowSettings:properties -version里sun.arch.data.model = 64更直观,这一项如果是 32,后面 ini 写得再对也起不来。用绝对路径而不是直接敲java,是为了排除 PATH 干扰,这一点在排查阶段特别重要:你敲的java和你以为的java经常不是同一个。

这一步确认完,JDK 的位置就固定下来了,比如C:\Java\jdk1.8.0_202。记下这个路径,下一步整段抄进 ini。JAVA_HOME配不配都行,MAT 不看它,只看启动器参数;但配了之后别的工具用起来方便,属于可选项,不影响这份包能不能跑。

3. 解压与改 ini:三行写错,前面全白干

3.1 解压后先认四样东西

这个包没有安装向导,解压完就等于装完了,剩下的全部工作量在配置文件上。解压到D:\tools\mat161之后,目录里东西不少,但真正要打交道的是四样:

  • MemoryAnalyzer.exe:启动器,双击它;它自己不实现分析功能,只负责读 ini、拉起 JVM。
  • MemoryAnalyzer.ini:唯一需要改的文件,JVM 路径和堆参数都在这。
  • plugins\:所有功能实现都在这里,包括 HPROF 解析器和报告生成器。
  • configuration\:运行时配置与日志,起不来的时候去里面翻.log。

其余features、p2之类的目录是 Eclipse 平台自带的,别删也别动。要改 ini 之前先复制一份MemoryAnalyzer.ini.bak,这条习惯救过我两次:Heap Dump 分析工具的参数一旦写坏,报错信息通常只有一句「JVM terminated」,看不到具体哪一行有问题,有备份就能立刻对比出来改了什么。

3.2 -vm 那两行:位置比内容更容易写错

打开 ini,你会看到-startup和--launcher.library两段,分别指向plugins下负责启动的 jar 和本地库。这两段是从包里带出来的,保持原样,不要动,也不要因为看着像路径就去改它。

真正要动的是-vm和-vmargs。下面这段是要插入/修改的部分:

# -vm 必须单独占一行,路径写在它的下一行,且整体必须放在 -vmargs 之前 -vm C:/Java/jdk1.8.0_202/bin/javaw.exe # -vmargs 之后才是给 JVM 的参数,这一段原文件里本来就有 -vmargs -Xms1g -Xmx8g

这几行里有三个容易翻车的点,全是血泪经验。第一,-vm和路径不能写在同一行,Eclipse 启动器读的是「下一行」作为值,写一行会被当成两个参数忽略掉。第二,-vm段必须出现在-vmargs之前,写在后面就不生效,启动器会退回 PATH 找 java,于是又回到版本不对的老路上。第三,路径里用/或者\\,不要加引号,"C:\Java\..."这种写法在 ini 里会被当成字面量的一部分。

还有个实用技巧:排查阶段把javaw.exe临时改成同一目录下的java.exe。javaw是不带控制台的窗口版,JVM 起不来的时候什么输出都没有,只能看到一闪;换成java之后会保留一个控制台窗口,Exit code=13、Could not reserve enough space这类原始错误能直接看到。等能正常启动了再换回javaw,省得每次开一个黑框。

3.3 -Xmx 怎么给:按 dump 大小算,不是越大越好

ini 里默认的堆通常只有 1024m,这个值拿来打开 500M 以内的小 dump 没问题,超过就是第 1 章那个「打开就 Out of memory」。改法很简单,-Xmx后面跟数值,但要给得合理:

dump 文件大小建议 -Xmx说明
500M 以内2g默认值也勉强能开,但别省这一步
1G ~ 2G4g大部分本地复现场景落在这里
3G ~ 5G8g需要 64 位 JVM,机器至少 12G 内存
8G 以上16g ~ 24g解析时间长,建议单独找一台机器
超过 30G拆分或换分布式方案见下方关于压缩指针的说明

经验值是-Xmx取 dump 大小的 1.5 到 2 倍。比例不是玄学:解析时 MAT 要把 dump 里的对象图搬进堆,还要额外建支配树、引用索引这些结构,1.5 倍是下限,2 倍比较稳。给少了会在解析到七八成的时候抛 OutOfMemoryError,给多了会拖慢 GC,也不见得更快。

还有一个 32G 的坎值得一提。64 位 HotSpot 在堆小于大约 32G 时会启用压缩指针,对象引用只占 4 字节;一旦-Xmx超过这个量级,压缩指针关掉,同样的对象图占用反而变大,等于加了堆没加到有效容量。所以除非 dump 特别大,-Xmx一般不会往 32G 以上给,给了也未必划算。

改完 ini 保存,注意用支持 UTF-8 无 BOM 的编辑器。Windows 记事本老版本保存 UTF-8 会带一个 BOM 头,启动器解析第一行参数时直接失败,现象是双击毫无反应、任务管理器里连进程都看不到,非常难查。这一点放到第 5 章展开。

4. 打开第一份 dump:向导选项、索引文件与报告怎么读

4.1 先抓一份自己能对得上的 dump

MAT 只是消费端,跑通验证的前提是手上有一份格式正确的 HPROF 文件。生产上抓 dump 有三种常见做法,我一般按场景挑:

# 方式一:jmap,JDK 8 环境里最常用 # live 表示先触发一次 Full GC 再 dump,能过滤掉已死对象,但会有一次停顿 jmap -dump:live,format=b,file=/data/dump/heap-$(date +%Y%m%d%H%M).hprof 12345 # 方式二:jcmd,参数更少,新一些的 JDK 上更稳 jcmd 12345 GC.heap_dump /data/dump/heap.hprof # 方式三:让 JVM 在 OOM 时自己落盘,事后取证用 java -Xmx512m -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/data/dump -jar app.jar

-dump:live这个参数要掂量着用。加上它,JVM 会先做一次 Full GC,只把还活着的对象写进文件,好处是 dump 小、分析快、误报少;代价是这次 Full GC 会停顿,对一个正被线上流量打着的进程来说,停顿时间可能比 dump 本身还长。我的习惯是:本地复现、容量足够的场景加live;线上紧急取证,先不加,宁可文件大一点,也别在故障现场再补一次长停顿。抓之前先df -h看一眼目标分区,dump 写到一半磁盘满,文件就是截断的,MAT 打不开。

jcmd和jmap的选择上,JDK 8 里两者都能用,jcmd的参数更少、出错提示更清楚。容器环境里要注意 attach 机制是否可用,挂载的/tmp不可写或者进程属主不一致时,attach 会直接失败,这跟 MAT 没关系,是抓取端的问题。

4.2 向导里四个选项,勾哪个不勾哪个

File → Open Heap Dump选完文件,会弹一个解析向导,大致是四个选项:Leak Suspects Report、Component Report、Re-open previously parsed heap dump,以及一个Keep unreachable objects勾选框。

我一般选 Leak Suspects Report,它会在解析完成后直接生成一份 HTML 报告,把占用最大的几个「可疑点」列出来,对第一次打开某份 dump 的人最省事。Component Report 是按组件维度做拆解,适合已经知道大概方向、想确认某个模块占用的时候用。第三个选项是给之前解析过、索引还在的 dump 用的,重开能省掉几分钟解析时间。

Keep unreachable objects默认不勾,多数情况保持不勾。不勾的时候,MAT 只保留从 GC Roots 可达的对象,解析更快、占用更少;勾上之后,连不可达对象也一起保留,排查「某个对象为什么迟迟没被回收」这类问题时才有意义,代价是堆占用明显上升,一份大 dump 可能因此打不开。选错了就重新打开一次文件,别在原会话里纠结。

4.3 索引文件写在哪,解析要多久

点击完成后,MAT 会在 dump 所在目录旁边生成一组索引文件,扩展名是.index、.domTree、.a2s、.o2h之类。这就是第 2 章强调磁盘空间和写权限的原因:目录只读、磁盘满、路径带特殊字符,都会让解析在最后一步失败,弹窗只写一句写文件失败,不告诉你是哪个文件。

解析耗时和 dump 大小基本成正比,1G 左右的 dump 在普通办公机上几分钟出报告,5G 以上做杯咖啡的工夫。进度条卡在 99% 长时间不动,通常是内存不够在硬扛 GC,这时候别等,直接杀掉进程,把-Xmx加上去重来。中断过的 dump 在下次打开时可能提示索引损坏,把同目录下那几个索引文件删掉,让它重新解析一次就行,dump 原文件不要动。

4.4 三个视图各自的用处

报告生成后,真正天天用的是三个地方。Overview 页给一张饼图,直观看到哪个线程或哪组对象吃了大头,属于第一眼定位。Histogram 按类统计实例数量和 Shallow Heap,适合确认「某个类是不是创建了异常多的实例」,比如一次请求里建了几十万个字符串。Dominator Tree 按 Retained Heap 排序,回答的是「谁真正占着这些内存」,一个对象自己很小但支配着几百兆,只有在这个视图里才看得出来。

Shallow Heap 和 Retained Heap 的区别值得单独记一笔:前者是对象自身占的字节数,后者是把这个对象干掉之后能释放的总量。排查内存泄漏看 Retained,调优对象布局看 Shallow。找到可疑对象后,右键Path to GC Roots,务必选exclude all phantom/weak/soft references,否则路径里全是各种弱引用包装,看不到真正把它拽住的那条强引用链。

5. 避坑与排查:五条真踩过的记录

5.1 启动阶段的三条

现象:双击后进程一闪而过,控制台版 java.exe 报JVM terminated. Exit code=13。原因:ini 里指定的 JVM 是 32 位,或者-vm路径写错了、指向了一个不存在的目录。启动器找不到匹配位数的 JVM,就直接退出,13 这个退出码基本就等于「JVM 不匹配」。 解决:把javaw.exe临时改成java.exe看原始输出,确认报的是位数问题后,用 2.2 节那两条命令验证目标 JDK 是不是 64 位,再检查-vm是不是单独一行、路径有没有引号、有没有写在-vmargs后面。

现象:启动器起来了,主界面加载到一半退出,configuration目录下的.log里一堆类找不到。原因:给 MAT 用了一个过新的 JDK。MAT 1.6.1 的平台是按 Java 8 编译的,拿 Java 11、17 去带,OSGi 层反射拿不到需要的类,插件一个个加载失败。 解决:不要动系统 PATH,只在 ini 里把-vm指向那个固定的 JDK 8 路径。机器上同时存在多个 JDK 是常态,靠 ini 隔离最干净,改 PATH 反而会影响其他项目。

现象:改完 ini 保存后再双击,什么都没发生,任务管理器里连进程都没有。原因:编辑器保存 ini 时写入了 BOM 头,启动器解析第一行参数就失败,失败得太早,来不及写日志。 解决:用支持 UTF-8 无 BOM 的编辑器重存一次,或者在十六进制视图里确认文件开头没有EF BB BF三个字节。这条坑最隐蔽,因为「什么反应都没有」最容易让人怀疑是包坏了。

5.2 解析阶段的另外两条

现象:打开 dump 后进度条走到一半弹 Out of memory,或者界面还在但点任何视图都卡死。原因:-Xmx相对 dump 大小给少了。也有一种情况是-Xmx给得够,但机器物理内存不足,操作系统开始换页,表现和 OOM 很像。 解决:按 3.3 节的表把-Xmx提到 dump 大小的 2 倍,同时用任务管理器看提交内存是否已经顶到上限。如果-Xmx已经要到 32G 以上,先考虑是不是能换个小一点的 dump、或者关闭Keep unreachable objects重开。

现象:提示Invalid HPROF file header或干脆说这不是一个堆转储文件。原因:抓取时进程还在写、磁盘写满、或者抓下来的根本不是 HPROF 格式(比如某些工具导出的自有格式、或者把 core dump 当堆转储用)。 解决:看文件大小是不是明显小于预期,重新用jmap或jcmd抓一次;确认抓取命令里没有漏掉format=b;如果是别的工具产出的文件,先转成标准 HPROF 再用 MAT 打开。

6. 装完怎么验证:拿一份 dump 走通「打开到出报告」全链路

装完不等于跑通。我的验收标准只有一条:能从双击图标开始,在没有人工干预的情况下,把一份真实的 dump 打开并生成 Leak Suspects 报告。达不到这条,其余都免谈。

验收按三步走,每步都有明确的判据,不通过时也知道该往哪儿看。

验证项操作期望结果不通过先看哪
启动双击 MemoryAnalyzer.exe5 秒内出主界面,无控制台报错ini 的 -vm 段;configuration下的 .log
解析File → Open Heap Dump,选 1G 以上的 dump进度条走完,Overview 页正常渲染-Xmx 是否 ≥ dump 的 1.5 倍;索引目录是否可写
出报告向导选 Leak Suspects Report生成 HTML 报告,可疑点有具体类名和占比是否勾了 Keep unreachable objects;索引文件是否完整

三步都过之后,再做一个反向验证:把-Xmx故意改回 1024m,重新打开同一份 dump,确认它确实会报 Out of memory。这一步看着多余,其实是把「配置项确实生效」这件事钉死,而不是碰巧因为别的原因跑通了。我有一次就是 ini 改错了位置但 dump 刚好够小,一路绿灯,直到换了个大文件才暴雷,前面省下的几分钟全赔进去了。

从那以后我每次装这套东西,都强制走一遍「小 dump 验证配置生效、大 dump 验证容量够用」的流程,再把 ini 备份留一份在目录里。这套流程不复杂,但它能把「打不开」这类模糊故障,拆成一个个能定位的具体项。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询