当我从本机 Java 方法引发C++异常时会发生什么情况?
假设我正在将 Sun 的 JVM 嵌入到一个C++应用程序中。通过JNI,我调用Java方法(我自己的),它又调用我在共享库中实现的本机方法。
如果此本机方法引发C++异常,会发生什么情况?
编辑:编译器是gcc 3.4.x,jvm是sun的1.6.20。
假设我正在将 Sun 的 JVM 嵌入到一个C++应用程序中。通过JNI,我调用Java方法(我自己的),它又调用我在共享库中实现的本机方法。
如果此本机方法引发C++异常,会发生什么情况?
编辑:编译器是gcc 3.4.x,jvm是sun的1.6.20。
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");
}
我们还有一个C++异常,用于引发新的 Java 异常:
//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);
}
}
如果你真的雄心勃勃,可以跟踪一个更大的函数,并获得部分堆栈跟踪。基本方法是为每个函数指定一个 ,并调用它们,将指向它们的指针推送到线程本地“callstack”上,当它们返回时,弹出指针。然后,更改 构造函数以制作该堆栈的副本,并将其传递给 。StackTraceElement[]
StackTraceElement
NewJavaException
setStackTrace
在 JNI 文献中,“异常”一词似乎专门用于指代 Java 异常。本机代码中的意外事件称为编程错误。JNI 明确不要求 JVM 检查编程错误。如果发生编程错误,则行为未定义。不同的 JVM 可能有不同的行为。
本机代码负责将所有编程错误转换为返回代码或 Java 异常。Java 异常不会立即从本机代码中引发。它们可以处于挂起状态,只有在本机代码返回到 Java 调用方时才引发。本机代码可以使用 检查挂起的异常,并使用 清除它们。ExceptionOccurred
ExceptionClear