关键不是猜原因,而是按堆、元空间、栈、直接内存等分层定位:查hs_err_pid*.log中“problematic frame”(jni越界)、“memory”小节(如metaspace已满)、“vm arguments”(缺-xx:maxmetaspacesize等),再用jstat/jcmd/jstack实时验证水位,结合nmt、heapdump等转储精准归因。

Java 中 JVM 频繁崩溃,若初步判断与内存结构相关(如堆、元空间、栈、直接内存等区域配置失当或使用异常),关键不是“猜原因”,而是按结构分层定位——哪块内存出问题,就查哪块的分配行为、上限设置和实际占用趋势。
看崩溃日志里明确指向哪块内存
打开 hs_err_pid*.log 文件,重点找三处:
-
“# Problematic frame” 或 “Current thread” 下的线程栈:如果报
SIGSEGV+ 出现在JNI调用中,大概率是堆外内存(DirectByteBuffer、MappedByteBuffer)越界或未释放; -
“Memory” 小节中的各区域使用量:比如
metaspace used = 248MB, committed = 256MB, max = 256MB,说明元空间已到上限,后续类加载会直接触发SIGABRT或OutOfMemoryError: Metaspace; -
“VM Arguments”:检查是否漏配关键参数,例如没设
-XX:MaxMetaspaceSize(导致元空间无限增长并耗尽本地内存)、没设-XX:MaxDirectMemorySize(Direct Buffer 泛滥引发 OOM Killer 杀进程)。
用运行时工具验证各内存区域真实水位
别只信监控图表,要抓实时快照比对:
-
堆内存:执行
jstat -gcutil <pid> 1000 5</pid>,观察OU(老年代使用率)是否持续 >95% 且 GC 后不下降; -
元空间:用
jstat -gccapacity <pid></pid>看MCMN/MCMX/MC(初始/最大/当前元空间容量),再结合jstat -gc <pid></pid>中MC和MU(已使用)差值是否逼近MCMX; -
直接内存:JDK 8u271+ 可用
jcmd <pid> VM.native_memory summary</pid>查Internal和Direct分项;或通过java.lang.management.ManagementFactory.getMemoryMXBean().getNonHeapMemoryUsage()在代码中打点监控; -
线程栈:用
jstack <pid> | grep "java.lang.Thread.State" | wc -l</pid>粗略统计活跃线程数,再对比ulimit -u系统限制,防止Unable to create new native thread。
针对性生成转储并分析异常模式
不同内存区域需不同转储方式:
-
堆溢出(OOM: Java heap space):确保启动加了
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/dump/,用 MAT 打开 hprof 文件,看 Dominator Tree 顶部是不是某个静态缓存、未关闭的流或大数组; -
元空间溢出(OOM: Metaspace):加
-XX:+PrintGCDetails -XX:+PrintGCTimeStamps日志,观察是否频繁打印Full GC (Metadata GC Threshold);再用jmap -clstats <pid></pid>查类加载器数量和平均类大小,确认是否由热部署、大量动态代理(如 Spring AOP、MyBatis Mapper)引起; -
堆外内存泄漏:启用 Native Memory Tracking(NMT),启动加
-XX:NativeMemoryTracking=detail,崩溃前执行jcmd <pid> VM.native_memory detail.diff</pid>对比两次快照,重点关注Internal和Direct的增长来源。
检查 JVM 参数与系统资源是否匹配
很多崩溃其实是“配置错位”导致的:
- 堆设了
-Xmx4g,但容器只分配了 3Gi 内存,Linux OOM Killer 可能直接 kill 进程(dmesg -T | grep -i "killed process"可验证); - 用了 G1GC 却没调
-XX:MaxGCPauseMillis,导致 STW 时间过长被监控误判为“假死”而重启; - 在容器中运行却未加
-XX:+UseContainerSupport(JDK 10+ 默认开启,旧版需手动),JVM 无法感知容器内存限制,仍按宿主机总内存计算堆大小,极易超限; - 元空间设了
-XX:MaxMetaspaceSize=512m,但应用加载了 3000+ 个 Spring Boot Starter,每个带几十个类,实际需要远超该值。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











