如何分析JVM崩溃?Java Web应用周期性崩溃排查步骤咨询
分析JVM周期性崩溃的一步步指南
Hey there! Dealing with a JVM crash that strikes every ~30 days is a classic "intermittent puzzle"—but with the right approach, you can track down the root cause. Let’s walk through the key steps:
1. 先抓住崩溃现场的核心证据
JVM崩溃时几乎一定会生成一个 hs_err_pid<pid>.log 文件(默认在应用工作目录或系统临时目录),这是你最关键的线索。重点拆解这些部分:
Problematic frame:直接指出崩溃发生的代码帧(可能是JVM自身模块、应用代码或第三方依赖)Physical Memory Usage:检查系统物理内存是否耗尽,排除系统层面的资源短缺Thread Stack Trace:查看崩溃瞬间所有线程的状态,找有没有死锁、无限循环或JNI调用异常Heap Summary:堆内存的使用快照,判断是不是OOM(内存溢出)触发的崩溃
如果之前没生成这个文件,赶紧给JVM加上参数:-XX:+PrintErrorFile,确保下次崩溃时能捕获到完整错误信息。
2. 排查内存泄漏(30天周期的常见元凶)
周期性崩溃大概率和内存泄漏有关——应用持续积累无法回收的对象,30天后堆内存耗尽触发崩溃:
- 提前开启堆转储:添加JVM参数
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/your/save/path,下次崩溃时会自动生成.hprof格式的堆转储文件 - 分析堆转储:用 Eclipse MAT 或 VisualVM 打开dump文件,重点找:
- 占用内存Top 10的对象类型
- 对象的引用链,定位哪些对象被长期持有(比如静态集合、未关闭的IO资源、缓存未清理)
- 有没有和月度业务相关的异常对象增长(比如月度报表生成的临时数据没被回收)
3. 检查线程与并发问题
有时候崩溃不是内存的锅,而是线程资源耗尽或并发冲突:
- 从
hs_err_pid文件里看所有线程状态:有没有线程长时间处于BLOCKED状态?有没有明确标注的死锁(Found one Java-level deadlock)? - 核对应用的定时任务:30天周期刚好对应月度任务,比如数据归档、账单生成,这些任务会不会创建大量线程,或占用CPU/内存导致系统过载?
4. 验证JVM参数配置合理性
不合理的JVM参数可能放大潜在问题:
- 检查堆大小:
-Xms(初始堆)和-Xmx(最大堆)是不是设置得太小?如果应用内存需求随时间增长,30天后触及上限就会崩溃 - 检查GC配置:有没有用适配业务的垃圾回收器?比如老年代用G1还是CMS?开启GC日志(
-Xloggc:/path/to/gc.log -XX:+PrintGCDetails),看GC频率是不是越来越高、耗时越来越长,最后导致Full GC无法回收内存而崩溃 - 确认关键参数没被禁用:比如
-XX:-HeapDumpOnOutOfMemoryError会让你丢失最核心的堆转储线索
5. 排查系统层面的外部因素
别忽略服务器本身的问题:
- 检查磁盘空间:30天后日志、临时文件占满磁盘,导致JVM无法写入日志或生成dump,进而崩溃
- 检查系统内存:有没有其他进程(比如备份任务、系统更新)在30天周期内占用大量内存,挤压JVM的可用空间?
- 检查CPU使用率:崩溃前是不是CPU被占满,导致JVM无法正常执行GC或应用代码?
6. 尝试重现问题(缩短排查周期)
30天等一次崩溃太耗时,试试这些方法加速验证:
- 模拟数据增长:手动导入大量测试数据,模拟30天的业务积累,看会不会提前触发崩溃
- 手动触发定时任务:如果怀疑是月度任务,直接执行任务,观察应用的内存、线程变化
- 压测验证:用JMeter等工具对应用进行高并发压测,看高负载下会不会暴露相同问题
最后补充:别漏看应用日志
崩溃前的应用日志里往往有预警信号——比如OOM警告、异常堆栈、资源耗尽报错,这些能帮你快速缩小排查范围。
内容的提问来源于stack exchange,提问作者Anjan Baradwaj
相关产品推荐
相关产品推荐

