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

Java 如何在任何OutOfMemoryException上退出JVM,即使坏人试图抓住它,java,jvm,Java,Jvm,OOME属于通常不应该从中恢复的错误类别。但是,如果它被隐藏在线程中,或者有人捕获了它,那么应用程序就有可能处于一种状态,即它没有退出,但没有用处。即使在使用可能愚蠢地试图捕捉可丢弃或错误/OOME的库时,关于如何防止这种情况的任何建议?(即您无法直接访问修改源代码)我能想到的唯一一件事是使用AOP将每个方法(注意不要使用java.*)包装为一个try catch for OOME,如果是这样,则记录一些内容并在catch块中调用System.exit() 这不是一个我称之为优雅的解决方案,但是

OOME属于通常不应该从中恢复的错误类别。但是,如果它被隐藏在线程中,或者有人捕获了它,那么应用程序就有可能处于一种状态,即它没有退出,但没有用处。即使在使用可能愚蠢地试图捕捉可丢弃或错误/OOME的库时,关于如何防止这种情况的任何建议?(即您无法直接访问修改源代码)

我能想到的唯一一件事是使用AOP将每个方法(注意不要使用java.*)包装为一个try catch for OOME,如果是这样,则记录一些内容并在catch块中调用System.exit()


这不是一个我称之为优雅的解决方案,但是…

如果应用程序JVM中的某段代码决定要尝试捕获OOME并尝试恢复,那么(不幸的是)您无法阻止它。。。除了可能不切实际的AOP英雄之外,AOP英雄肯定对应用程序的性能和可维护性有害。除此之外,您所能做的最好的事情就是使用“onAutofmemoryError”钩子拔掉JVM上的插头。见上面的答案:

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

  • 在库方法中调用
    System.exit()
    deep
  • 调用
    Thread.stop()
    和朋友
  • 泄漏开放流、数据库连接等
  • 产生大量线程
  • 随机挤压(即捕捉并忽略)异常
  • 等等
实际上,在其他人编写的代码中发现类似问题的方法是使用代码质量检查器,并执行代码检查

如果问题在第三方代码中,则将其报告为BUG(可能是),如果他们不同意,则开始寻找替代方案


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

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

  • 如果应用程序是多线程的,那么OOME也有可能被抛出到其他线程上,这使得恢复更加困难

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

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


  • 从OOME恢复对解决根本原因没有任何作用,这些根本原因通常是内存泄漏、设计不良(即内存浪费)的数据结构和/或启动应用程序时堆太小。

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

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


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

    您可以使用运行java程序。然而,这假设“坏人”很好地记录了错误:)

    一种可能性,我很想被谈论掉,就是有一个愚蠢的线程,它的工作就是在堆上做一些事情。如果它接收到OOME,那么它将退出整个JVM

    请告诉我这是不明智的

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

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

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

  • 编译它。(有趣的是javac 不管它在包
    java.lang
    )中

  • 将类添加到JRE
    rt.jar

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

  • 这是一种你可能想知道的可能性。这是一个好主意,甚至是合法的,这是另一个问题。

    解决方案:

    在较新的JVM上:

    -XX:+ExitOnOutOfMemoryError
    to exit on OOME, or to crash:
    
    -XX:+CrashOnOutOfMemoryError
    
    关于老年人:

    -XX:OnOutOfMemoryError="<cmd args>; <cmd args>"
    
    用户发布了一条评论,这应该是用户自己的答案。新的JVM特性使这变得简单,特别是

    -XX:+ExitOnOutOfMemoryError
    
    要在OOME上退出或崩溃:

    -XX:+CrashOnOutOfMemoryError
    

    因为Java 8u92听起来很痛苦;)而且,没有办法区分哪些代码有好的OOME捕捉器,哪些代码有坏的OOME捕捉器。这不会增加太多开销。问题是,如果尝试捕获是在某种模糊方法的中间,你就无能为力了。但是,如果异常发生在库/应用程序的某个较低级别,并且某个较高的方法捕获了它,那么AOP将在这里工作。就像我说的,我想不出还有什么对这个案子有效:)“……你无能为力。”嗯,实际上可能有。。。如果您知道发生这种情况的确切方法,请再次求助于援救:配置切入点以完全转移调用该方法的流程(但大多数情况下这是不可能的,尤其是在需要上下文或使用实例变量的实例方法上)。这确实会很痛苦,但这主要是因为它不是正确的AOP解决方案。您可以编写切入点来拦截捕获块,这意味着您的方面只有t
    -XX:+CrashOnOutOfMemoryError