何时捕获 java.lang.Error?

Posted

技术标签:

【中文标题】何时捕获 java.lang.Error?【英文标题】:When to catch java.lang.Error? 【发布时间】:2010-09-26 01:13:06 【问题描述】:

在什么情况下应该在应用程序中捕获java.lang.Error

【问题讨论】:

另见***.com/questions/2679330/… 【参考方案1】:

一般来说,从不。

但是,有时您需要捕获特定错误。

如果您正在编写类似于框架的代码(加载第 3 方类),最好捕获 LinkageError(未找到类定义、不满意的链接、不兼容的类更改)。

我还看到一些愚蠢的第 3 方代码抛出 Error 的子类,所以你也必须处理这些。

顺便说一句,我不确定是否无法从OutOfMemoryError 中恢复。

【讨论】:

我必须完全加载 DLL,如果配置不正确,将会失败。在此应用程序的情况下不是致命错误。 捕捉 OutOfMemoryError 有时是有意义的——例如,当您创建大型数组列表时。 @SpaceTrucker:这种方法在多线程应用程序中是否有效,或者是否存在其他线程中较小的分配因此而失败的重大风险? …大概只有当你的数组足够小可以分配,但没有给其他人留下任何东西时。 @PJTraill 我不确定。这将需要一些真实世界的统计样本。我以为我见过这样的代码,但不记得它在哪里。 如果通过抛出该错误已解锁大量内存供 GC 处理,则很有可能从 OutOfMemoryError 中恢复。然而,这表明代码或 JVM 设置存在重大缺陷,因此不应轻易完成。【参考方案2】:

从来没有。您永远无法确定应用程序能够执行下一行代码。如果你得到OutOfMemoryError,你就有no guarantee that you will be able to do anything reliably。捕获 RuntimeException 和已检查的异常,但从不捕获错误。

http://pmd.sourceforge.net/rules/strictexception.html

【讨论】:

永远不要说永远。我们有执行“断言错误”的测试代码;然后捕获 AssertionError 以确保设置了 -ea 标志。除此之外......是的,可能永远不会;-) 如何将请求传递给工作线程的服务器应用程序。在工作线程上捕获 Throwable 以捕获任何错误并至少尝试记录出现的问题可能没有意义吗? 从不...除非您绝对需要。从不是一个强有力的词,规则总是有例外。如果您正在构建一个框架,那么您必须捕获和处理某些错误的可能性不大,即使它只是为了记录。 错误怎么样?来自第三方库方法的 NoSuchMethodError? @OutlawProgrammer 仅作记录,还有其他方法可以做同样的测试:boolean assertionsEnabled = false; assert assertionsEnabled = true;【参考方案3】:

通常,您应该始终捕获java.lang.Error 并将其写入日志或显示给用户。我在支持部门工作,每天都看到程序员无法分辨程序中发生了什么。

如果你有一个守护线程,那么你必须防止它被终止。在其他情况下,您的应用程序将正常工作。

您应该只在***别捕获java.lang.Error

如果您查看错误列表,您会发现大部分都可以处理。例如,在读取损坏的 zip 文件时会出现 ZipError

最常见的错误是OutOfMemoryErrorNoClassDefFoundError,在大多数情况下它们都是运行时问题。

例如:

int length = Integer.parseInt(xyz);
byte[] buffer = new byte[length];

可以生成OutOfMemoryError,但这是一个运行时问题,没有理由终止您的程序。

NoClassDefFoundError 主要发生在不存在库或使用另一个 Java 版本时。如果它是您程序的可选部分,那么您不应终止您的程序。

我可以举更多例子说明为什么在顶层捕获 Throwable 并生成有用的错误消息是个好主意。

【讨论】:

我怀疑在这些情况下最好让守护进程失败并发出适当的警报,然后让它有可能作为失败的 jvm 上的一些以太幽灵保持活力,在那里它可能会给出错误的借口真的还活着,正在做某事 OutOfMemoryError 不是运行时错误,无法保证应用程序可以从中恢复。如果幸运的话,您可能会在new byte[largeNumber] 中获得 OOM,但如果该分配不足以导致 OOM,则可能会在下一行或下一个线程中触发。这是运行时问题,因为如果 length 是不受信任的输入,则应在调用 new byte[] 之前对其进行验证。 NoClassDefFoundError 可以出现在任何地方,因为当编译的java 代码找不到类时会调用它。如果您的 JDK 配置错误,它可能会因尝试使用 java.util.* 类而触发,并且几乎不可能针对它进行编程。如果您可以选择包含依赖项,则应使用ClassLoader 来检查它是否存在,这会抛出ClassNotFoundException ZipError 表示包含类的 jar 文件是损坏的 zip 文件。这是一个非常严重的问题,此时您不能信任任何被执行的代码,尝试从中“恢复”将是不负责任的事情。 一般来说,在顶层捕获java.lang.Errorjava.lang.Throwable 并尝试用它做一些事情可能会有所帮助——比如记录一条错误消息。但在这一点上,不能保证这会得到执行。如果您的 JVM 正在 OOM,则尝试记录可能会分配更多 Strings,从而触发另一个 OOM。【参考方案4】:

在多线程环境中,您最常想捕捉它!当您捕获它时,记录它并终止整个应用程序!如果您不这样做,则可能正在执行某些关键部分的某些线程将死掉,而应用程序的其余部分将认为一切正常。因此,可能会发生许多不需要的情况。 一个最小的问题是,如果其他线程由于一个线程不工作而开始抛出一些异常,您将无法轻松找到问题的根源。

例如,通常循环应该是:

try 
   while (shouldRun()) 
       doSomething();
   

catch (Throwable t) 
   log(t);
   stop();
   System.exit(1);

即使在某些情况下,您也希望以不同的方式处理不同的错误,例如,在 OutOfMemoryError 上,您将能够定期关闭应用程序(甚至可能释放一些内存,然后继续),在其他一些情况下,您没有太多可以。

【讨论】:

捕捉OutOfMemoryError 并继续而不是立即存在是不明智的,因为your program is then in an undefined state。 calling system,exit on catch a throwable 会产生意想不到的后果,即杀死 JVM 上运行的所有内容,而不仅仅是相关应用程序。对于可能与其他 Web 应用程序一起托管在应用服务器上的 Web 应用程序,这通常不是一个好的做法(更不用说它会影响应用服务器本身)。【参考方案5】:

很少。

我会说只在线程的顶层,以便尝试发出一条消息,说明线程死亡的原因。

如果您所在的框架为您执行此类操作,请将其留给框架。

【讨论】:

【参考方案6】:

几乎没有。错误被设计为应用程序通常无法解决的问题。唯一的例外可能是处理错误的呈现,但即使这样也可能不会按计划进行,具体取决于错误。

【讨论】:

【参考方案7】:

Error 通常不应被捕获,因为它表示不应该发生的异常情况

来自Error 类的 Java API 规范:

ErrorThrowable 的子类 这表明严重的问题 合理的应用不应该尝试 去抓。大多数此类错误是 异常情况。 [...]

方法不需要声明 它的 throws 子句的任何子类 期间可能抛出的错误 执行该方法但不执行 捕获,因为这些错误是 不应该出现的异常情况 发生。

正如规范所述,Error 仅在以下情况下被抛出 很可能,当出现Error 时,应用程序几乎无能为力,并且在某些情况下,Java 虚拟机本身可能处于不稳定状态(例如VirtualMachineError

虽然ErrorThrowable 的子类,这意味着它可以被try-catch 子句捕获,但它可能不是真正需要的,因为当@ 出现时应用程序将处于异常状态987654335@被JVM抛出。

在Java Language Specification, 2nd Edition 的11.5 The Exception Hierarchy 部分中还有一个关于此主题的简短部分。

【讨论】:

【参考方案8】:

如果您足够疯狂地创建一个新的单元测试框架,您的测试运行程序可能需要捕获任何测试用例抛出的 java.lang.AssertionError。

否则,请参阅其他答案。

【讨论】:

【参考方案9】:

还有其他几种情况,如果您发现错误,您必须重新抛出它。例如ThreadDeath 永远不应该被捕获,如果你在封闭的环境(例如应用程序服务器)中捕获它可能会导致大问题:

应用程序仅在必须清理时才应捕获此类的实例 异步终止后。如果 ThreadDeath 被方法捕获, 重要的是要重新抛出它,这样线程才能真正死掉。

【讨论】:

这实际上不是问题,因为您只是 not catch Errors.【参考方案10】:

非常非常罕见。

我只为一个非常具体的已知案例做这件事。 例如,如果两个 independence ClassLoader 加载相同的 DLL,则可能会抛出 java.lang.UnsatisfiedLinkError。 (我同意我应该将 JAR 移动到共享类加载器)

但最常见的情况是您需要登录才能知道用户投诉时发生了什么。您希望向用户发送消息或弹出窗口,而不是默默地死掉。

即使是 C/C++ 程序员,他们也会在退出前弹出错误并告诉人们不理解的内容(例如内存故障)。

【讨论】:

【参考方案11】:

在一个 android 应用程序中,我遇到了 java.lang.VerifyError。我正在使用的库无法在具有旧版本操作系统的设备中运行,并且库代码将引发此类错误。我当然可以通过在运行时检查操作系统的版本来避免错误,但是:

未来支持的最旧 SDK 可能会针对特定库发生变化 try-catch 错误块是更大的回退机制的一部分。一些特定的设备,虽然它们应该支持库,但会抛出异常。我捕获了 VerifyError 和所有异常以使用回退解决方案。

【讨论】:

【参考方案12】:

在测试环境中捕获 java.lang.AssertionError 非常方便...

【讨论】:

你想增加什么价值? 添加未中止的测试套件的值【参考方案13】:

理想情况下,我们不应该处理/捕获错误。但是根据框架或应用程序的要求,可能存在我们需要做的情况。假设我有一个 XML Parser 守护进程,它实现了消耗更多内存的 DOM Parser。如果有一个要求,比如 Parser 线程在得到 OutOfMemoryError 时不应该死掉,而是应该处理它并向应用程序/框架的管理员发送消息/邮件。

【讨论】:

【参考方案14】:

理想情况下,我们不应该在 Java 应用程序中捕获 Error,因为它是一种异常情况。应用程序将处于异常状态,并可能导致乱码或给出一些严重错误的结果。

【讨论】:

【参考方案15】:

在检查断言的单元测试中捕获错误可能是合适的。如果有人禁用断言或以其他方式删除您想知道的断言

【讨论】:

【参考方案16】:

当 JVM 不再按预期工作或即将运行时,会出现错误。如果你捕捉到一个错误,并不能保证catch块会运行,更不能保证它会运行到最后。

还要看运行的电脑,当前的内存状态,所以没办法测试,尽力而为吧。你只会有一个危险的结果。

您还将降低代码的可读性。

【讨论】:

以上是关于何时捕获 java.lang.Error?的主要内容,如果未能解决你的问题,请参考以下文章

Java 异常处理

Java异常

是啥导致 java.lang.***Error

线程“主”java.lang.***Error actionListeners 中的异常

Java - 线程“主”java.lang.Error 中的异常:未解决的编译问题

java.lang.***Error 递归目录