有没有一种方法可以为我的Java应用程序构建全局OutOfMemoryError
监听器?
我想在OOM时优雅地停止JVM(try-catch
不是一个选项)。
有没有一种方法可以为我的Java应用程序构建全局OutOfMemoryError
监听器?
我想在OOM时优雅地停止JVM(try-catch
不是一个选项)。
由于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));
}
}
很遗憾,当总堆使用量超过阈值时,没有简单的方法可以接收通知,因此,我们必须在每个支持阈值(通常是老年代)的内存池上安装一个阈值,并在接收通知时重新检查总使用量,以触发总使用量超过阈值时的关机。
还有一种作为JVM命令行选项使用的ExitOnOutOfMemoryError选项。
-XX:OnOutOfMemoryError
ExitOnOutOfMemoryError - 当你启用这个选项时,JVM会在第一次出现内存溢出错误时退出。如果你更喜欢重新启动JVM实例而不是处理内存溢出错误,可以使用它。
我认为这并不是一个真正的优雅的JVM退出,关闭挂钩不会运行。
OutOfMemoryError
是一个严重问题,无法合理处理。但是,您可以通过注册JVM关闭挂钩在它关闭之前执行一些操作。 - Andrew Tobilko