代码之家  ›  专栏  ›  技术社区  ›  Idan K

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

  •  22
  • Idan K  · 技术社区  · 14 年前

    假设我将Sun的JVM嵌入到C++应用程序中。通过JNI,我调用一个Java方法(我自己的),它反过来调用我在共享库中实现的本机方法。

    如果本机方法抛出C++异常会发生什么?

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

    5 回复  |  直到 14 年前
        1
  •  5
  •   Rob Kennedy    14 年前

    在JNI文献中 例外 似乎专门用于引用Java异常。本机代码中的意外事件称为 . JNI显式地不要求jvm检查编程错误。如果发生编程错误,则行为未定义。不同的jvm可能有不同的行为。

    悬而未决的 ,只在本机代码返回到Java调用方时抛出。本机代码可以检查 ExceptionOccurred ExceptionClear .

        2
  •  24
  •   Mooing Duck    8 年前

    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) {}
    };
    

    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
        }
    };
    

    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[] 得到一个部分stacktrace。基本方法是给每个函数 StackTraceElement NewJavaException 复制堆栈并将其传递给 setStackTrace

        3
  •  3
  •   André Caron    14 年前

    我认为这是不明确的行为。将异常传播回C代码(这就是运行JVM的原因)是未定义的行为。

    在Windows上,编译器必须使用微软的结构化异常处理来实现异常,因此C++异常将通过C代码安全地被捕获。 ,C代码是 不是写在心里的例外

    在其他平台上,嗯,我不知道,但不能再漂亮了。在编写JNI代码时,我将每个C++函数封装在一个 try 布洛克:即使我没有 throw std::bad_alloc 我想到了,但其他的也是可能的)。

        4
  •  3
  •   Dima    14 年前

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

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

        5
  •  2
  •   josefx    14 年前

    JNI使用c函数与本机代码接口。C不能正确地处理异常,因为它不知道它们的存在。因此,您必须捕获本机代码中的异常并将其转换为java异常,否则jvm将崩溃。(这是因为java异常只在本机代码返回java时抛出)