当我从本地Java方法抛出C ++exception时会发生什么?

假设我将Sun的JVMembedded到C ++应用程序中。 通过JNI,我调用一个Java方法(我自己的),它依次调用我在共享库中实现的本地方法。

如果这个本地方法抛出一个C ++exception,会发生什么?

编辑:编译器是gcc 3.4.x,jvm是sun的1.6.20。

Solutions Collecting From Web of "当我从本地Java方法抛出C ++exception时会发生什么?"

在JNI文献中, 异常这个词似乎只是用来指Java异常。 本地代码中的意外事件被称为编程错误 。 JNI显然不需要JVM来检查编程错误。 如果发生编程错误,行为是不确定的。 不同的JVM可能会有不同的表现。

将所有编程错误转换为返回代码或Java异常是本地代码的责任。 Java异常不会立即从本机代码中抛出。 它们可以挂起 ,只有当本机代码返回到Java调用者时才抛出。 本机代码可以使用ExceptionOccurred检查暂挂的异常,并使用ExceptionOccurred清除它们。

Java编译器不理解C ++异常,因此您必须同时处理Java和C ++异常。 幸运的是,这并不复杂。 首先,我们有一个C ++异常,告诉我们是否发生了Java异常。

 #include <stdexcept> //This is how we represent a Java exception already in progress struct ThrownJavaException : std::runtime_error { ThrownJavaException() :std::runtime_error("") {} ThrownJavaException(const std::string& msg ) :std::runtime_error(msg) {} }; 

以及在Java异常已经存在的情况下抛出C ++异常的函数:

 inline void assert_no_exception(JNIEnv * env) { if (env->ExceptionCheck()==JNI_TRUE) throw ThrownJavaException("assert_no_exception"); } 

我们也有抛出新的Java异常的C ++异常:

 //used to throw a new Java exception. use full paths like: //"java/lang/NoSuchFieldException" //"java/lang/NullPointerException" //"java/security/InvalidParameterException" struct NewJavaException : public ThrownJavaException{ NewJavaException(JNIEnv * env, const char* type="", const char* message="") :ThrownJavaException(type+std::string(" ")+message) { jclass newExcCls = env->FindClass(type); if (newExcCls != NULL) env->ThrowNew(newExcCls, message); //if it is null, a NoClassDefFoundError was already thrown } }; 

我们还需要一个函数来吞服C ++异常,并将其替换为Java异常

 void swallow_cpp_exception_and_throw_java(JNIEnv * env) { try { throw; } catch(const ThrownJavaException&) { //already reported to Java, ignore } catch(const std::bad_alloc& rhs) { //translate OOM C++ exception to a Java exception NewJavaException(env, "java/lang/OutOfMemoryError", rhs.what()); } catch(const std::ios_base::failure& rhs) { //sample translation //translate IO C++ exception to a Java exception NewJavaException(env, "java/io/IOException", rhs.what()); //TRANSLATE ANY OTHER C++ EXCEPTIONS TO JAVA EXCEPTIONS HERE } catch(const std::exception& e) { //translate unknown C++ exception to a Java exception NewJavaException(env, "java/lang/Error", e.what()); } catch(...) { //translate unknown C++ exception to a Java exception NewJavaException(env, "java/lang/Error", "Unknown exception type"); } } 

通过上述功能,可以很容易地在C ++代码中使用Java / C ++混合异常,如下所示。

 extern "C" JNIEXPORT void JNICALL Java_MyClass_MyFunc(JNIEnv * env, jclass jc_, jstring param) { try { //do not let C++ exceptions outside of this function //YOUR CODE BELOW THIS LINE. HERES SOME RANDOM CODE if (param == NULL) //if something is wrong, throw a java exception throw NewJavaException(env, "java/lang/NullPointerException", "param"); do_stuff(param); //might throw java or C++ exceptions assert_no_exception(env); //throw a C++ exception if theres a java exception do_more_stuff(param); //might throw C++ exceptions //prefer Java exceptions where possible: if (condition1) throw NewJavaException(env, "java/lang/NullPointerException", "condition1"); //but C++ exceptions should be fine too if (condition0) throw std::bad_alloc("BAD_ALLOC"); //YOUR CODE ABOVE THIS LINE. HERES SOME RANDOM CODE } catch(...) { //do not let C++ exceptions outside of this function swallow_cpp_exception_and_throw_java(env); } } 

如果你真的有雄心壮志,可以跟踪更大函数的StackTraceElement[] ,并获得部分栈跟踪。 基本的方法是给每个函数一个StackTraceElement ,当它们被调用的时候,将一个指针指向一个线程本地的“callstack”,当它们返回时,将指针关闭。 然后,修改NewJavaException的构造NewJavaException来创建该堆栈的副本,​​并将其传递给setStackTrace

我猜你的JVM会崩溃。 本地C ++异常不会通过JNI传播到Java中。 其中一个原因是JNI是一个C接口,C对C ++异常一无所知。

在进入JNI代码的C层之前,你需要做的就是捕捉C ++异常,并使JNI C函数返回一个错误代码。 然后,您可以检查Java内部的错误代码,并在必要时抛出Java异常。

我会将其标记为未定义的行为。 将异常传播回C代码(这就是运行JVM)是​​未定义的行为。

在Windows上,编译器必须使用Microsoft的结构化异常处理来实现异常,所以C ++异常将通过C代码“安全地”进行。 但是 ,C代码并不是在写出例外的情况下编写的 ,所以如果你幸运的话,你将会崩溃,如果不是,那么就会出现不一致的状态和资源泄漏。

在其他平台上,好吧,我不知道,但不能更漂亮。 当我编写JNI代码时,我将每个C ++函数都封装在一个try块中:即使我没有throw ,我仍然可能会得到一些标准的异常( std::bad_alloc ,但也有可能)。

JNI使用c函数来与本地代码进行交互。 C不能正确地处理异常,因为它不知道它们的存在。 所以你必须捕捉你的本地代码中的异常,并将它们转换成Java异常,否则你的jvm将崩溃。 (这是可行的,因为只有当本地代码返回到java时才抛出java异常)