1

我正在开发一个非常庞大且复杂的 C++ 应用程序,该应用程序在几个不相关的地方使用 JNI 来完成不同的任务。平台是 RHEL 6。因为我不知道应用程序是否已经创建了 JNI,所以我使用这种方法来查找 JVM 并附加到它:

JOI_RetCode JavaObjectInterface::initJVM()
{
  if (jvm_ == NULL)
  {
    // Is there an existing JVM already created?
    jsize jvm_count = 0;
    jint result = JNI_GetCreatedJavaVMs (&jvm_, 1, &jvm_count);
    if (result != JNI_OK)
      return JOI_ERROR_CHECK_JVM;      

    if (jvm_count == 1)
    {
      // Yes - use it.
      result = jvm_->GetEnv((void**) &jenv_, JNI_VERSION_1_6);
      switch (result)
      {
        case JNI_OK:
          logger::log(CAT_JNI, LL_DEBUG, "Attached to an existing JVM.");
          jenv_ = env;
          break;

        case JNI_EDETACHED:
          result = jvm_->AttachCurrentThread((void**) &jenv_, NULL);   
          if (result != JNI_OK)
            return JOI_ERROR_ATTACH_JVM;

          needToDetach_ = true;
          logger::log(CAT_JNI, LL_DEBUG, "Attached to an existing JVM from another thread.");
          break;

        case JNI_EVERSION:
          logger::log(CAT_JNI, LL_DEBUG, "Attaching to a JVM of the wrong version.");
          return JOI_ERROR_JVM_VERSION;
          break;

        default:
          logger::log(CAT_JNI, LL_DEBUG, "Unknown error Attaching to an existing JVM.");
          return JOI_ERROR_ATTACH_JVM;
          break;
      }
    }
    else
    {
      // No - create a new one.
      result = createJVM();
      if (result != JNI_OK)
        return JOI_ERROR_CREATE_JVM;

      needToDetach_ = false;
      logger::log(CAT_JNI, LL_DEBUG, "Created a new JVM.");
    }
  }

  return JOI_OK;
}

JVM 指针被定义为类数据成员,如下所示:

JavaVM*   jvm_;
JNIEnv*   jenv_;

现在,当在我调用该方法之前在其他地方创建 JVM 时,结果是对 JNI_GetCreatedJavaVMs() 的调用返回一个 JVM 指针,对 GetEnv() 的调用返回 JNI_OK,并使用看起来有效的指针更新 jenv_。到目前为止,一切都很好。但是,当我接下来这样做时:

  jclass javaClass = jenv_->FindClass(className); 

我抛出异常。我无法从异常本身获取任何数据,因为 stderr 被重定向到 /dev/null,所以如果我调用 ExceptionDescribe() ,输出将进入地狱。询问异常对象需要一个有效的 JNI 来调用 FindClass("Throwable"),所以这也不起作用。

对 FindClass() 的调用很好,因为当我创建第一个 JVM 时,它运行良好。

谁能帮我找到我必须在这里某个地方犯的愚蠢错误?谢谢,尤瓦尔。

4

1 回答 1

2

JNIEnv指针通常是线程特定的。使用 JVM 参考来按需查找是最简单的。

对于需要访问的任何代码块,JNIEnv在该本地块的范围内查找它:

JNIEnv* jenv_;
int result = jvm_->GetEnv((void**) &jenv_, JNI_VERSION_1_6);
if (result == JNI_EDETACHED) {
    // attach
}
// Do what you gotta do here
// ...
if (needs_detach) {
    // detach
}

这样,JNIEnv无论您的上下文如何,您都一定会获得有效的。

主要的收获是“不要缓存JNIEnv”。

于 2013-05-30T18:02:15.980 回答