即使坏人试图抓住它,如何在任何OutOfMemoryException上退出JVM

OOME是一类错误,通常你不应该从中恢复。 但是如果它被隐藏在一个线程中,或者有人捕获它,那么应用程序可能会进入一个它不会退出的状态,但是没有用处。 有关如何防止这种情况的任何建议,即使面对使用可能愚蠢地尝试捕获Throwable或Error / OOME的库? (即您没有直接访问权限来修改源代码)

解:

-XX:OnOutOfMemoryError="; " 

定义:首次抛出OutOfMemoryError时运行用户定义的命令。 (在1.4.2更新12,6中引入)

请参阅http://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html

杀死正在运行的进程的示例:

 -XX:OnOutOfMemoryError="kill -9 %p" 

如果应用程序的JVM中的某些代码决定它想要捕获OOME并尝试恢复,那么(不幸的是)你可以做任何事情来阻止它…除了可能不切实际的AOP英雄,以及绝对不利于您的应用程序的性能和可维护性。 除此之外,您可以做的最好的事情是使用“OnOutOfMemoryError”挂钩来拔出JVM上的插件。 请参阅上面的答案: https : //stackoverflow.com/a/3878199/139985/

基本上,你必须相信其他开发人员不要做愚蠢的事情。 你可能不应该试图防范的其他愚蠢的事情包括:

  • 在库方法深处调用System.exit()
  • 调用Thread.stop()和朋友,
  • 泄漏开放流,数据库连接等,
  • 产生大量的线程,
  • 随机挤压(即捕捉和忽略)exception,
  • 等等

实际上,在其他人编写的代码中解决这类问题的方法是使用代码质量检查器,并执行代码审查。

如果问题出现在第三方代码中,请将其报告为BUG(可能是它),如果他们不同意,请开始寻找备选方案。


对于那些还不知道这一点的人来说,尝试从OOME恢复是个坏主意有很多原因:

  1. 当前线程正在更新一些重要的数据结构时,可能会抛出OOME。 在一般情况下,捕获此OOME的代码无法知道这一点,如果它试图“恢复”,则存在应用程序将继续使用损害数据结构的风险。

  2. 如果应用程序是multithreading的,那么OOME也有可能被抛到其他线程上,使得恢复更加困难。

  3. 即使应用程序可以在不使数据结构处于不一致状态的情况下进行恢复,恢复也可能只会导致应用程序瘫痪几秒钟然后再次出现OOME。

  4. 除非你适当地设置JVM选项,否则几乎耗尽内存的JVM往往花费大量时间进行垃圾收集,徒劳地试图继续这样做。 试图从OOME恢复可能会延长痛苦。

从OOME恢复没有解决根本原因,通常是内存泄漏,设计不当(即内存浪费)数据结构,和/或使用太小的堆启动应用程序。

  1. 编辑OutOfMemoryError.java ,在其构造函数中添加System.exit()

  2. 编译它。 (有趣的是,javac不关心它在包java.lang

  3. 将该类添加到JRE rt.jar

  4. 现在jvm将使用这个新类。 (邪恶的笑)

您可能希望了解这种可能性。 这是一个好主意,甚至是合法的,是另一个问题。

我能想到的另一件事(虽然我不知道如何实现它)将是在某种调试器中运行你的应用程序。 我注意到,我的调试器可以在抛出exception时停止执行。 🙂

因此可能可以实现某种执行环境来实现这一点。

如果您不关心系统如何退出,只要它退出,那么在启动脚本/命令中传递-XX:+HeapDumpOnOutOfMemoryError JVM开关可能有效吗?

如何在代码和System.exit()自己捕获OOME?

您可以使用带有OutOfMemory检测filter的 Java Service Wrapper运行Java程序。 但是,这假设“坏人”足以记录错误:)

我很乐意谈论的一种可能性是有一个愚蠢的线程,工作就是在堆上做一些事情。 应该接收OOME – 然后它退出整个JVM。

请告诉我这不明智。

当程序超过设置的堆分配阈值时,可以使用MemoryPoolMXBean进行通知。

我自己没有使用它,但是当通过设置分配阈值并在收到通知时调用System.exit()时剩余内存变低时,应该可以关闭此方法。

我唯一能想到的就是使用AOP来包装每个方法(小心排除java。*)和一个针对OOME的try-catch,如果是这样,记录一下并在catch块中调用System.exit()。

虽然不是我称之为优雅的解决方案……