JNI - 从本机线程

时间:2016-05-31 09:36:45

标签: java c++ c exception java-native-interface

如果从本机创建的线程中使用JNIEnv :: ThrowNew()抛出Java异常会发生什么?

如果从Java调用抛出Java异常的本机代码,那么异常会按预期返回到Java端,但如果有人在没有直接或间接来自Java的情况下调用此本机代码会怎样?显然,它不会回到Java,因为我们不是从那里来的。

我不是想这样做,因为一些未定义或奇怪的行为不能解释我究竟发生了什么。

谢谢。

2 个答案:

答案 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-8155881JDK-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的同时释放调用堆栈,那么可能应该没问题;运气好的话,未捕获的异常将与其他任何异常一样处理。但是为了增加安全性,您应该自己捕获异常。