如果从本机创建的线程中使用JNIEnv :: ThrowNew()抛出Java异常会发生什么?
如果从Java调用抛出Java异常的本机代码,那么异常会按预期返回到Java端,但如果有人在没有直接或间接来自Java的情况下调用此本机代码会怎样?显然,它不会回到Java,因为我们不是从那里来的。
我不是想这样做,因为一些未定义或奇怪的行为不能解释我究竟发生了什么。
谢谢。
答案 0 :(得分:3)
如果本机代码正在进行Java调用,则必须调用AttachCurrentThread()
以获取JNI env
值,如果以没有JNI {{1}的方式调用本机代码传递给它。
因此,在本机线程或调用结束之前,它必须调用env
。但是,如果有任何挂起的Java异常,DetachCurrentThread()
将调用由@EJP发布的DetachCurrentThread()
。
根据JNI specification regarding exceptions:
异常处理
有两种方法可以处理本机代码中的异常:
本机方法可以选择立即返回,导致在启动本机的Java代码中抛出异常 方法调用。
本机代码可以通过调用ExceptionClear()清除异常,然后执行自己的异常处理代码。
引发异常后,必须首先清除本机代码 在进行其他JNI调用之前的异常。当有待处理时 例外,可以安全调用的JNI函数是:
Thread.UncaughtExceptionHandler
请注意,对于任何挂起的Java异常, ExceptionOccurred()
ExceptionDescribe()
ExceptionClear()
ExceptionCheck()
ReleaseStringChars()
ReleaseStringUTFChars()
ReleaseStringCritical()
Release<Type>ArrayElements()
ReleasePrimitiveArrayCritical()
DeleteLocalRef()
DeleteGlobalRef()
DeleteWeakGlobalRef()
MonitorExit()
PushLocalFrame()
PopLocalFrame()
对所有人都不安全。
还有一个previously called AttachCurrentThread()
must call DetachCurrentThread()
的原生帖子:
附加到VM
JNI接口指针(JNIEnv)仅在当前有效 线。如果另一个线程需要访问Java VM,它必须 首先调用
DetachCurrentThread()
将自己附加到VM和 获取JNI接口指针。一旦附加到VM,就是原生的 线程就像在本机中运行的普通Java线程一样工作 方法。本机线程保持连接到VM,直到它调用AttachCurrentThread()
分离自己。附加的线程应该有足够的堆栈空间来执行 合理的工作量。每个线程的堆栈空间分配是 特定于操作系统。例如,使用pthreads,堆栈大小 可以在
DetachCurrentThread()
的{{1}}参数中指定。从VM中分离
附加到VM的本机线程必须在退出之前调用
pthread_attr_t
以自行分离。如果线程无法自行分离,则 调用堆栈上有Java方法。
因此,本机代码必须在退出之前调用pthread_create
,但是在Java异常待处理的情况下无法安全地调用DetachCurrentThread()
。
答案 1 :(得分:1)
在Java 8和更早版本中,唯一的JNI functions that are safe to call with a pending exception是那些释放锁/引用或它们本身与异常处理相关的对象。这个想法是,当将JVM异常传递给本机代码时,应该要么捕获异常本身,要么将堆栈展开为将要调用的调用框架。如果一个线程没有这样的调用框架可展开,那么它应该执行前者。因此,处理本机线程中未捕获的异常的唯一符合规范的方法是在调用ExceptionClear
之前(可能是在将其传递给某些自定义处理程序之后)将其清除(即调用DetachCurrentThread
)。
实际上,如果未捕获的未处理异常进入了DetachCurrentThread
调用,则HotSpot VM始终通过将其传递给未捕获的异常,以与JVM管理的线程相同的方式处理它们。处理程序并终止线程。问题是,JNI规范没有提供本机线程中未捕获异常的方法,以这种方式推迟JVM实现的未捕获异常处理。
这种缺陷在JDK-8155881和JDK-8179050这两个标签中被注意到,但是对于其他实现来说,要求HotSpot行为被认为太麻烦了。作为一种折衷,在Java 9中,对JNI规范进行了修改,以使DetachCurrentThread
is safe to call with a pending exception,但the effect of this is implementation-defined:'如果在调用DetachCurrentThread
时出现异常,VM可以选择报告其存在。 ”。 remains there in JNI 15这样写。
长话短说,如果您在释放所有资源直至DetachCurrentThread
的同时释放调用堆栈,那么可能应该没问题;运气好的话,未捕获的异常将与其他任何异常一样处理。但是为了增加安全性,您应该自己捕获异常。