C++捕获所有异常

330

有没有C++的等价物来替代Java的

try {
    ...
}
catch (Throwable t) {
    ...
}

我正在尝试调试调用本地Windows函数的Java/jni代码,但虚拟机一直崩溃。本地代码在单元测试中看起来很好,只有通过jni调用时才会崩溃。一个通用的异常捕获机制将非常有用。


1
如何构建一个C++ DLL包装器来捕获所有异常? - lsalamon
4
请注意,大多数崩溃不是由C ++中的异常引起的。您可以捕获所有异常,但这并不能防止许多崩溃发生。 - Mooing Duck
如果您来到这里,您可能正在寻找以下内容:https://dev59.com/z2kv5IYBdhLWcg3w_lzE#32799720 https://www.man7.org/linux/man-pages/man2/sigaction.2.html https://www.man7.org/linux/man-pages/man7/signal.7.html - Andrew
16个回答

10

请注意

try{
// ...
} catch (...) {
// ...
}

只捕捉语言级别的异常,其他低级别的异常/错误如访问冲突段错误将不会被捕捉。


2
诸如“分段错误”之类的问题实际上不是异常,而是信号;因此,您不能像处理典型异常那样捕获它们。但是,有一些解决方法,例如这个 - MAChitgarha
catch(...)catch(std::exception)有什么区别? - Mayur
1
@Mayur catch(...) 可以捕获所有语言级别抛出的异常,而 catch(std::exception) 只能捕获类型为 std::exception 的异常。例如,如果您调用 throw 5,只有 catch(...) 可以捕获该异常。 - muaz
捕获所有异常是否会有额外的费用? - Maf
1
@Maf异常处理并非免费,与无异常或错误处理相比,它具有成本。但如果您的意思是:捕获所有异常与捕获特定异常相比较便宜,因为不需要进行异常类型检查,但这只是一种没有证据支持的观点。 - muaz
@muaz 我会说,如果你的处理成本较低,那么在这种情况下,你需要更多的内存成本来执行所有的异常。 - Maf

6

一种通用的异常捕获机制将非常有用。

这是值得怀疑的。因为代码会崩溃,你已经知道你的代码出了问题。吞掉异常可能会掩盖这个问题,但这可能只会导致更难以发现的错误。

你真正需要的是一个调试器...


15
我不同意,实时应用程序中有许多情况下我宁愿捕获未知异常,将任何内容写入日志/执行一些通用的错误处理操作,而不是让应用程序崩溃。 - f0ster
3
我认为你可能在考虑一些情况,其中你可以追求某些通用的错误处理方式,方便地忽略了那些堆栈已损坏或内存已耗尽且通用的错误处理也将无法成功的情况。捕获您可以从中恢复的错误是没有问题的,但在我看来,一个万能异常处理应该只作为孤立的(独立的堆栈、预分配的内存)仔细编写的逻辑存在于程序终止之前被调用;如果您不知道问题所在,就不能确定它是否可以被恢复。 - Shog9
1
安装一个信号处理程序,该程序在运行时取消一些日志以确定程序崩溃的位置和原因。 - Clearer
例如,我有一套单元测试。如果一个测试失败了,我想要记录它,然后继续测试 - Edward Falk
@Shog9 我完全不同意。你知道在崩溃时代码已经出了问题,但不知道具体哪里出了问题。如果代码正在生产环境中运行,你需要记录日志以便知道发生了什么。用户信息通常是无用的:他们不知道自己做了什么,一切都是随机的。如果你不知道问题出在哪里,几乎不可能找到它。 - Offler
这就是为什么你真正想要记录任何可用的信息并终止,@offler。核心转储并不好玩,但肯定比用户更少出错。 - Shog9

4
  1. 您是否可以从控制台窗口运行使用JNI的Java应用程序(从java命令行启动),以查看在JVM崩溃之前是否有任何报告。如果直接作为Java窗口应用程序运行,可能会错过在控制台窗口中运行时出现的消息。

  2. 其次,您是否可以存根化您的JNI DLL实现,以显示来自JNI的方法正在被调用,您正在正确返回等?

  3. 以防问题是由于C++代码中某个JNI接口方法的错误使用导致的,您是否已验证一些简单的JNI示例是否与您的设置编译并正常工作?我特别考虑使用JNI接口方法将参数转换为本机C++格式并将函数结果转换为Java类型。存根这些方法很有用,以确保数据转换正常工作,并且您不会在调用JNI接口的COM样式调用中出错。

  4. 还有其他要检查的事情,但是如果不了解您的本机Java方法是什么以及它们的JNI实现试图做什么,很难建议任何内容。从C++代码级别捕获异常是否与您的问题相关尚不清楚。(您可以使用JNI接口将异常重新抛出为Java异常,但是从您提供的信息来看,这是否有所帮助还不确定。)


3

针对无法正确调试使用JNI的程序(或者运行时调试器中未出现错误的情况)的真正问题:

在这种情况下,经常有助于在JNI调用周围添加Java包装器(即所有本地方法都是私有的,您类中的公共方法调用它们),以执行一些基本的健全性检查(检查所有“对象”是否被释放,“对象”在释放后是否被使用)或同步(只需将一个DLL中的所有方法与单个对象实例同步)。让Java包装器方法记录错误并抛出异常。

这通常有助于更轻松地找到真正的错误(令人惊讶的是,大多数情况下是Java代码不遵守调用函数的语义,导致某些难以处理的双重释放或类似的问题)而不是尝试在本地调试器中调试大规模并行的Java程序...

如果您知道原因,请在包装器方法中保留避免出错的代码。最好由您的包装器方法抛出异常,而不是您的JNI代码崩溃VM...


2
如果您正在寻找与Windows有关的解决方案,则可以使用结构化异常处理: https://learn.microsoft.com/en-us/cpp/cpp/try-except-statement 代码如下:
__try
{
   // code here may throw or make access violation
}
__except( EXCEPTION_EXECUTE_HANDLER )
{
    // after exception code here, e.g. log the error
}

它不仅可以捕获C++异常,还可以捕获访问冲突或其他系统异常。

1
这主要取决于编译器环境。 gcc无法捕获这些异常。 Visual Studio和我使用的最后一个Borland可以。 因此,关于崩溃的结论取决于您开发环境的质量。 C++规范指出catch(...)必须捕获任何异常,但并非在所有情况下都是如此。 至少根据我的尝试。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接