jni不自动转换c++异常,未捕获的std::exception越过jni边界会导致jvm崩溃;必须在jni函数入口用try/catch兜底,通过env->thrownew()主动抛java异常,并立即return,且jnienv*仅限当前线程有效。

Java层看不到C++抛出的std::exception?
JNI本身不自动把C++异常转成Java异常,std::exception或任何未捕获的C++异常一旦越过JNI边界,会直接导致JVM崩溃(常见报错:Fatal error: EXCEPTION_ACCESS_VIOLATION 或 sigsegv)。这不是“没映射”,而是根本没机会映射——异常必须在C++函数返回前被显式拦截。
- 所有JNI函数入口(如
Java_com_example_Foo_nativeMethod)都必须用try/catch(...)兜底,哪怕只做日志+退出 - 不要依赖
std::set_terminate或信号处理器来补救——JVM可能已处于不可恢复状态 - 如果用RAII资源(如
std::unique_ptr),确保catch块里不抛新异常,否则二次崩溃
如何从C++主动抛出一个Java异常?
要用env->ThrowNew(),不是throw,也不是env->Throw()(后者要求异常对象已存在)。关键是类名格式:必须是斜杠分隔的内部形式,且类必须已加载。
-
env->ThrowNew(env->FindClass("java/lang/IllegalArgumentException"), "invalid handle")—— 正确 -
env->ThrowNew(env->FindClass("java.lang.IllegalArgumentException"), ...)—— 错误,点号不合法 -
env->FindClass()失败时返回nullptr,必须检查,否则ThrowNew会触发JNI_ERR - 自定义异常类需确保在Java启动时已由系统类加载器或当前线程上下文类加载器加载,否则
FindClass找不到
为什么ThrowNew后函数没立刻返回,Java却收到异常?
env->ThrowNew()只是“设置”异常标志,不会中断执行流。C++函数仍会继续跑完,直到返回JNI层。JVM在函数返回后检查该标志,再决定是否将控制权交还给Java并抛出对应异常。
- 务必在
ThrowNew后加return(或goto cleanup),否则后续代码可能访问无效内存、重复释放资源 - 不要在
ThrowNew后调用env->ExceptionCheck()来判断——它返回JNI_TRUE,但此时异常尚未被Java处理,状态不稳定 - 如果函数有多个出口(如不同
if分支),每个分支在抛异常后都得显式return,漏一个就埋雷
JNIEnv*失效导致异常注册失败?
JNIEnv*是线程局部的,仅对当前调用线程有效。跨线程(比如C++回调、异步任务)直接用主线程保存的env指针调用ThrowNew,结果是未定义行为——大概率静默失败,或触发JNI DETECTED ERROR IN APPLICATION日志。
立即学习“Java免费学习笔记(深入)”;
- 需要跨线程抛异常时,先用
JavaVM*(全局唯一)调用AttachCurrentThread()获取当前线程的JNIEnv* - 用完后必须配对调用
DetachCurrentThread(),尤其在线程长期存活场景下,否则可能泄漏线程绑定 - Android上
AttachCurrentThread在低版本有性能开销,避免高频调用;可考虑把错误信息传回Java线程再由Java侧统一抛出








