它可能有效,但通常是个坏主意。无法保证您的应用程序将成功恢复,或者它会知道它是否没有成功。例如:
-
确实可能没有足够的内存来执行请求的任务,即使在采取了恢复步骤(例如释放保留的内存块)之后也是如此。在这种情况下,您的应用程序可能会陷入循环,在该循环中它反复出现恢复,然后再次耗尽内存。
-
OOME 可以在任何线程上抛出。如果应用程序线程或库不是为处理它而设计的,这可能会使某些长期存在的数据结构处于不完整或不一致的状态。
-
如果线程因 OOME 而死,应用程序可能需要在 OOME 恢复过程中重新启动它们。至少,这会使应用程序更加复杂。
-
假设一个线程使用通知/等待或更高级别的机制与其他线程同步。如果该线程因 OOME 而死,则其他线程可能会等待永远不会到来的通知(等)......例如。为此进行设计可能会使应用程序变得更加复杂。
总之,设计、实现和测试应用程序以从 OOME 中恢复可能很困难,特别是如果应用程序(或运行它的框架,或它使用的任何库)是多线程的。最好将 OOME 视为致命错误。
另请参阅my answer 相关问题:
编辑 - 回答这个后续问题:
换句话说,如果在应用程序服务器 (jboss/websphere/..) 中抛出 OOME,我是否必须重新启动它?
不,您不必必须重新启动。但这可能是明智的,尤其是如果您没有一种好的/自动化的方式来检查服务是否正常运行。
JVM 将恢复正常。但是应用程序服务器和应用程序本身可能会或可能不会恢复,这取决于它们设计用于应对这种情况的程度。 (我的经验是,一些应用程序服务器不是为了解决这个问题而设计的,设计和实现一个复杂的应用程序来从 OOME 中恢复是很困难的,正确地测试它就更难了。)
编辑 2
回应此评论:
“其他线程可能会等待通知(等)永远不会到来” 真的吗?被杀死的线程不会展开它的堆栈,释放资源,包括持有的锁吗?
是的,真的!考虑一下:
线程 #1 运行:
synchronized(lock) {
while (!someCondition) {
lock.wait();
}
}
// ...
线程 #2 运行:
synchronized(lock) {
// do something
lock.notify();
}
如果线程#1 正在等待通知,并且线程#2 在// do something 部分获得了OOME,那么线程#2 将不会进行notify() 调用,并且线程#1 可能会一直等待对于永远不会发生的通知。当然,线程 #2 保证会释放 lock 对象上的互斥锁……但这还不够!
如果不是线程运行的代码不是异常安全的,这是一个更普遍的问题。
“异常安全”不是我听说过的术语(尽管我知道你的意思)。 Java 程序通常不会被设计为对意外异常具有弹性。确实,在上述情况下,要使应用程序异常安全,可能很难甚至不可能。
您需要一些机制,从而将线程#1 的故障(由于 OOME)转变为线程#2 的线程间通信失败通知。 Erlang 会这样做……但 Java 不会。他们可以在 Erlang 中做到这一点的原因是 Erlang 进程使用严格的类似 CSP 的原语进行通信。即没有数据结构的共享!
(请注意,对于任何意外异常,您都可能遇到上述问题......不仅仅是Error异常。有某些类型的Java代码试图从中恢复>unexpected 异常可能会以糟糕的方式结束。)