如何监听OutOfMemoryError并退出JVM

时间:2018-01-08 09:08:13

标签: java java-8 out-of-memory

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

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

3 个答案:

答案 0 :(得分:13)

您可以使用JVM option

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

并执行您选择的命令

答案 1 :(得分:6)

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

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

但在某些情况下,就像通过ExecutorService执行的操作一样,所有throwable都会被自动捕获。不过,在这些情况下,应用程序中应该有一些代码可以评估结果并处理特殊情况。

但也许,你想在之前做出反应要迟到,例如在没有足够的内存来保存未决数据之前保存。对此的解决方案将进入以下方向:

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

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

答案 2 :(得分:5)

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

-XX:OnOutOfMemoryError
  

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

它不是一个优雅的 JVM退出,我不认为关机挂钩运行。