本文大纲
《JVM剖析及性能跟踪》
实践篇:当 JVM 内存溢出时的回调处理
在 Java 应用开发中,内存溢出(OutOfMemoryError)是一个常见的问题,它会导致应用性能下降,甚至崩溃。及时有效地处理内存溢出问题,是每个 Java 开发者和运维人员必备的技能。本文将介绍 JVM 提供的几个处理内存溢出的参数,并结合实际案例,讲解如何在内存溢出时进行回调处理,帮助你快速定位和解决内存问题。
目标
- 了解 JVM 处理内存溢出的相关参数
- 掌握在内存溢出时进行回调处理的方法
- 提高处理 Java 应用内存溢出问题的能力
主要参数
- -XX:+HeapDumpOnOutOfMemoryError 和 -XX:HeapDumpPath
- -XX:OnOutOfMemoryError
- -XX:+ExitOnOutOfMemoryError
- -XX:+CrashOnOutOfMemoryError
下面我们来详细的看下每个参数的意义和用法。
1. -XX:+HeapDumpOnOutOfMemoryError 和 -XX:HeapDumpPath
当 JVM 发生内存溢出时,-XX:+HeapDumpOnOutOfMemoryError 参数会触发 JVM 自动生成堆转储文件(Heap Dump),而 -XX:HeapDumpPath 参数则指定堆转储文件的存储路径。
什么是 Heap Dump?
Heap Dump 是 JVM 堆内存的快照,它包含了内存中所有对象的详细信息,包括对象的类型、大小、引用关系等。通过分析 Heap Dump,我们可以:
- 定位内存泄漏的原因
- 优化内存使用,减少内存浪费
- 提升应用性能
如何使用?
在启动 JVM 时,添加以下参数:
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/path/to/heapdump.hprof
当发生内存溢出时,JVM 会在指定路径生成一个 .hprof 文件,这就是 Heap Dump 文件。
如何分析 Heap Dump?
常用的 Heap Dump 分析工具有:
- Eclipse Memory Analyzer (MAT):功能强大,支持泄漏检测、对象统计等功能。
- HeapHero:可视化分析工具,支持多种内存分析技术。
2. -XX:OnOutOfMemoryError
-XX:OnOutOfMemoryError 参数允许我们在 JVM 发生内存溢出时,执行一个自定义的 shell 脚本或命令。
大多数时候,内存溢出并不会导致整个应用都Crash掉,但可能你需要在这里把应用重启一下。
为什么需要它?
内存溢出可能会导致应用处于不稳定状态,甚至崩溃。通过执行自定义脚本,我们可以:
- 优雅地关闭应用
- 重启应用
- 发送告警通知
如何使用?
在启动 JVM 时,添加以下参数:
-XX:OnOutOfMemoryError="/path/to/script.sh"
当发生内存溢出时,JVM 会执行指定的脚本。
当给JVM传递上述参数的时候,如果发生了内存溢出,JVM会调用shell脚本,在这个脚本中你可以去用优雅的办法来重启你的应用。
3.-XX:+CrashOnOutOfMemoryError
-XX:+ExitOnOutOfMemoryError 参数会在 JVM 发生内存溢出时,强制退出 JVM。同时,JVM会产生文本和二进制格式的崩溃日志。
为什么需要它?
在某些场景下,我们希望应用在发生内存溢出时立即退出,而不是继续运行在不稳定的状态。
但是,我是不建议配置上这个参数的,我们应该是以一种优雅的方式退出程序,粗暴的退出方式可能会损害正在进行的事务。
注意事项
- 该参数不会生成 Heap Dump 文件,也不会执行
-XX:OnOutOfMemoryError指定的脚本。 - 退出时不会执行 finally 块和关闭钩子,可能会导致资源未释放。
某个应用配置了-XX:+CrashOnOutOfMemoryError这个参数,当发生内存溢出的时候,JVM立马就退出了,并且在控制台有如下日志打印:
Aborting due to java.lang.OutOfMemoryError: GC overhead limit exceeded
#
# A fatal error has been detected by the Java Runtime Environment:
#
# Internal Error (debug.cpp:308), pid=26064, tid=0x0000000000004f4c
# fatal error: OutOfMemory encountered: GC overhead limit exceeded
#
# JRE version: Java(TM) SE Runtime Environment (8.0_181-b13) (build 1.8.0_181-b13)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (25.181-b13 mixed mode windows-amd64 compressed oops)
# Failed to write core dump. Minidumps are not enabled by default on client versions of Windows
#
# An error report file with more information is saved as:
# C:\workspace\tier1app-svn\trunk\buggyapp\hs_err_pid26064.log
#
# If you would like to submit a bug report, please visit:
# http://bugreport.java.com/bugreport/crash.jsp
日志中可以看出来,在C:\workspace\tier1app-svn\trunk\buggyapp\hs_err_pid26064.log目录下生成了崩溃日志文件,它里面包含了崩溃的详细信息。有些工具(fastThread)可以用来分析这个日志, 但是大多数时候,这些信息都很基础,根本无法定位内存溢出的原因。
4.-XX:+ExitOnOutOfMemoryError
如果传递了这个参数,当发生内存溢出的时候,JVM就会立马退出。如果你想在发生内存溢出的时候关闭应用那么可以使用这个参数。我一般也不会使用这个参数,道理同上。
我之前也遇到过配置了这个参数的应用,在发生内存溢出之后应用就退出了,不同的是这次JVM任何日志也没有产生,直接就退出了。
总结
JVM 提供了多个处理内存溢出的参数,我们可以根据实际需求进行选择和配置。建议在生产环境中:
- 配置
-XX:+HeapDumpOnOutOfMemoryError和-XX:HeapDumpPath,以便在内存溢出时生成 Heap Dump 文件,便于分析问题。 - 配置
-XX:OnOutOfMemoryError,在内存溢出时执行自定义脚本,例如重启应用。 - 不建议配置
-XX:+ExitOnOutOfMemoryError和-XX:+CrashOnOutOfMemoryError,除非有特殊需求。
通过合理配置这些参数,我们可以更有效地处理 Java 应用的内存溢出问题,提升应用的稳定性和可靠性。