直接按异常类型反向定位内存区域:java.lang.OutOfMemoryError: Java heap space→堆;Metaspace→元空间;StackOverflowError→虚拟机栈;unable to create new native thread→本地线程资源;GC overhead limit exceeded→堆与GC策略。

直接按异常类型反向定位内存区域,比从头背结构更高效。遇到报错别先翻文档,先看错误关键词——它已经告诉你该盯哪个区了。
一看错误信息,锁定问题区域
每类 OOM 或 Stack 错误都对应一个明确的内存区域,这是排错的第一锚点:
- java.lang.OutOfMemoryError: Java heap space → 堆(Heap):对象分配过多、内存泄漏、年轻代/老年代比例失衡
- java.lang.OutOfMemoryError: Metaspace → 方法区(JDK 8+ 的 Metaspace):动态生成类太多(如 Spring CGLIB、JSON 反序列化、热部署)、类加载器未卸载
- java.lang.StackOverflowError → 虚拟机栈:单线程递归过深、方法调用链过长(不一定是死循环,也可能是深层嵌套的模板渲染或规则引擎)
- java.lang.OutOfMemoryError: unable to create new native thread → 本地方法栈 + 系统资源:线程数超限(JVM 线程栈总占用 > 系统可用虚拟内存),常因线程池未复用、异步任务无节制创建线程导致
- GC overhead limit exceeded → 堆 + GC 策略:98% 时间在 GC,却只回收不到 2% 内存,本质是堆太小或对象长期存活挤占空间
二查关键指标,验证初步判断
光看报错不够,要用命令快速验证区域状态是否真“爆”了:
- 堆水位:用 jstat -gc
查 OU(OldUsed) 和 OU/OC(老年代使用率),持续 >90% 是强信号 - 元空间压力:jstat 输出中 MU(MetaspaceUsed) 接近 MC(MetaspaceCapacity),且 Full GC 后不下降,说明类没被卸载
- 线程数量:用 ps -T -p
| wc -l 或 jstack| grep 'java.lang.Thread' | wc -l ,对比系统 ulimit -u 限制值 - 栈帧深度:jstack 输出里看最长调用链(连续 at 行数),超过 1000 层大概率触发 StackOverflowError
三配最小必要参数,快速收口
不是所有参数都要调,只动最相关、副作用最小的那几个:
- 堆溢出:优先加 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/heap.hprof,再考虑调 -Xms/-Xmx(注意设相等避免扩容抖动)
- Metaspace 溢出:加 -XX:MaxMetaspaceSize=256m(防无限增长),配合 -XX:+PrintGCDetails 观察是否频繁 GC 类元数据
- 栈溢出:若确认非代码缺陷,可微调 -Xss256k(默认 1MB,多数业务 256–512k 足够);但更应检查是否有无意的深度递归
- 线程数超限:必须查代码中 new Thread() 或未配置 core/max 的线程池,参数上可设 -XX:ThreadStackSize=256(仅辅助,治标不治本)
四留现场,避免重启失据
线上出问题第一反应不是重启,而是固化证据:
- OOM 自动导出堆转储后,立即用 jmap -histo
快速看大对象分布(比打开 hprof 更快) - 发生 StackOverflow 或频繁 GC 时,执行 jstack
> jstack.out 和 jstat -gc -h101000 10 > gc.log (每秒采样10次) - 确认 hs_err_pid*.log 是否生成(JVM 崩溃必有),路径可通过 -XX:ErrorFile=/var/log/jvm/hs_err_%p.log 显式指定











