如何监听 OutOfMemoryError 并退出 JVM

How to listen for OutOfMemoryError and exit the JVM

有没有办法为我的 Java 应用构建全局 OutOfMemoryError 侦听器?

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

您可以使用JVM option

-XX:OnOutOfMemoryError="<cmd args>; <cmd args>"

并执行您选择的命令

由于 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));
    }
}

不幸的是,当总堆使用量超过阈值时,无法简单地接收通知,因此,我们必须在支持它的每个内存池(通常是老一代)上安装一个阈值并重新检查总使用量通知,当总使用量超过阈值时触发关闭。

还有 ExitOnOutOfMemoryError option 用作 JVM 命令行选项。

-XX:OnOutOfMemoryError

ExitOnOutOfMemoryError - When you enable this option, the JVM exits on the first occurrence of an out-of-memory error. It can be used if you prefer restarting an instance of the JVM rather than handling out of memory errors.

这不是真正的优雅 JVM 退出,我不认为关闭钩子运行。