如何监听OutOfMemoryError并退出JVM

12

有没有一种方法可以为我的Java应用程序构建全局OutOfMemoryError监听器?

我想在OOM时优雅地停止JVM(try-catch不是一个选项)。


4
在OOM错误发生时,你会发现JVM会自动退出。 - Boris the Spider
你是否试图利用熔断攻击? - Hesham Yassin
实际上我的应用程序没有因为OOM而停止,这就是我来这里寻求帮助的原因 :) - rellocs wood
1
如果您的应用程序没有在OOM上停止,那么您(或库)正在捕获它(并且可能会打印一些消息,否则您将不知道)。添加有关实际问题的一些详细信息,也许有解决方案。 - zapl
你说的“优雅地停止JVM”是什么意思?OutOfMemoryError是一个严重问题,无法合理处理。但是,您可以通过注册JVM关闭挂钩在它关闭之前执行一些操作。 - Andrew Tobilko
3个回答

16
你可以使用 JVM选项
-XX:OnOutOfMemoryError="<cmd args>; <cmd args>"

并执行您选择的命令


8

由于OutOfMemoryError通常不会被捕获,因此最简单的方法之一是

Thread.setDefaultUncaughtExceptionHandler((thread,t) -> {
    if(t instanceof OutOfMemoryError) {
        System.exit(1);
    }
});

但在某些情况下,例如通过ExecutorService执行的操作,所有可抛出异常都会自动捕获。然而,在这些情况下,您的应用程序中仍应存在一些代码来评估结果并处理异常情况。

但也许,您希望在为时已晚之前就做出反应,例如在甚至没有足够的内存保存挂起数据之前保存数据。解决方案将走向以下方向:

MemoryMXBean mBean = ManagementFactory.getMemoryMXBean();
((NotificationEmitter)mBean).addNotificationListener(
    (n, mb) -> {
        MemoryUsage mu = ((MemoryMXBean)mb).getHeapMemoryUsage();
        if(mu.getUsed()*100/mu.getMax() > 80)
            System.out.println("more than 80% used");// may initiate a shut down
    },
    n -> n.getType().equals(MemoryNotificationInfo.MEMORY_COLLECTION_THRESHOLD_EXCEEDED),
    mBean);

for(MemoryPoolMXBean pool : ManagementFactory.getMemoryPoolMXBeans()) {
    if(pool.getType() == MemoryType.HEAP && pool.isCollectionUsageThresholdSupported()) {
        pool.setCollectionUsageThreshold((int)Math.floor(pool.getUsage().getMax()*0.8));
    }
}

很遗憾,当总堆使用量超过阈值时,没有简单的方法可以接收通知,因此,我们必须在每个支持阈值(通常是老年代)的内存池上安装一个阈值,并在接收通知时重新检查总使用量,以触发总使用量超过阈值时的关机。


我们有一个类似的工具,当虚拟机达到一定堆百分比时,会杀死虚拟机并启动新的虚拟机。我不知道这是如何实现的,感谢您提供的提示。 - Eugene

5

还有一种作为JVM命令行选项使用的ExitOnOutOfMemoryError选项

-XX:OnOutOfMemoryError

ExitOnOutOfMemoryError - 当你启用这个选项时,JVM会在第一次出现内存溢出错误时退出。如果你更喜欢重新启动JVM实例而不是处理内存溢出错误,可以使用它。

我认为这并不是一个真正的优雅的JVM退出,关闭挂钩不会运行。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接