获取 Java VM 参考,从 C++ 和并发创建 Java 对象

lit*_*net 4 c++ java java-native-interface jvm

我得到了以下场景:C++ 服务器应用程序正在侦听传入的客户端连接。每次客户端连接尝试都会生成一个新会话。此会话将调用特定服务,具体取决于服务 id,该服务 id 在来自客户端的序列化数据中提供。一旦结果从服务到达,会话就会将数据发送到客户端。

该场景的缺陷在于,该服务是用 Java 实现的。

因此我的问题是:

  1. 如果新的客户端请求到达,如何使用来自 C++ 的 VM 引用实例化和调用 Java 服务类?

我知道为此我需要一个 Java VM。并且因为 C++ 服务器类将首先从 Java 应用程序调用(通过 SWIG 生成的包装器),所以我想我可能会将这个应用程序的 VM 引用传递给我的服务器类(以及之后的会话)。

但:

  1. 如何在我的 Java 代码中获取对当前 VM 的引用?

通常,Java 应用程序在启动服务器后什么也不做。也许我将不得不让它保持空闲状态以保持 VM 引用处于活动状态?对于 C++ 和 Java 交互中对服务的并发调用(超出服务内部的正常并发处理),我有什么特别需要担心的吗?

例子:

//Java Service
public class JMyService{
  public String loadContactInformation(int userid){
        return "test";
  }
}

//C++ (very simplified)
class Session{    
   public:
      //[...]
      void handleWrite(){
            vm = getVMReference(); //is saved beforehand
            if(serviceId == CONTACT_INFO){                     
                 //todo call JMyService.loadContactInformation
            }
      }
}
Run Code Online (Sandbox Code Playgroud)

我已经看到了这个问题,但我不得不承认,这个解决方案很难理解,而且还不清楚提问者想要达到什么目的。在这篇文章中,作者正在做一些与 Java 内置类型类似的事情,但似乎代码生成器不能用于自己的 Java 类型。我也知道可以生成一个新的 VM来完成这项工作,但如果可能的话,我想使用现有的VM

编辑

到 1) 我不确定,但是jint JNI_OnLoad(JavaVM *vm, void *reserved);当我使用 C++ 服务器类加载库时,也许可以使用该方法来获取指向 VM 的指针。不幸的是,Oracle 文档没有解释这个问题。有人可能有这方面的经验吗?

apa*_*gin 6

调用 API + JNI 函数会有所帮助。

如何在我的 Java 代码中获取对当前 VM 的引用?

  1. 打电话JNI_GetCreatedJavaVMs获取JavaVM*参考。如果 JVM 已在当前进程中启动,则此函数通常返回一个正好包含一个 JVM 引用的数组。
  2. 如果当前线程不是从 Java 创建的,请JavaVM->AttachCurrentThread使用在步骤 1 中获得的引用进行调用。如果当前线程已与 JVM 关联,​​则跳过此步骤。
  3. 调用JavaVM->GetEnv以获取JNIEnv*当前线程的指针。使用JNIEnv结构,您将能够调用 JNI 函数,请参见下文。

如何使用 C++ 中的 VM 引用实例化和调用 Java 服务类

  1. 使用JNIEnv->FindClass得到jclass你想要实例化一个Java类的引用。
  2. 调用JNIEnv->GetMethodID以获取对类的构造函数的引用。默认构造函数(没有参数的构造函数)有"()V"签名。
  3. 从步骤 1 和 2调用JNIEnv->NewObject传递jclassjmethodID实例化给定的类。
  4. 运行JNIEnv->CallObjectMethod以执行 Java 方法。obj参数是在步骤3中获得的对象引用,methodID参数是您要调用的方法GetMethodID

示例代码

    JavaVM* vm;
    jsize vmCount;
    if (JNI_GetCreatedJavaVMs(&vm, 1, &vmCount) != JNI_OK || vmCount == 0) {
        fprintf(stderr, "Could not get active VM\n");
        return NULL;
    }

    JNIEnv* env;
    jint result = vm->GetEnv((void**)&env, JNI_VERSION_1_6);
    if (result == JNI_EDETACHED) {
        result = vm->AttachCurrentThread((void**)&env, NULL);
    }
    if (result != JNI_OK) {
        fprintf(stderr, "Failed to get JNIEnv\n");
        return NULL;
    }

    jclass cls = env->FindClass("JMyService");
    jmethodID ctor = env->GetMethodID(cls, "<init>", "()V");
    jobject service = env->NewObject(cls, ctor);

    jmethodID loadMethod = env->GetMethodID(cls, "loadContactInformation", "(I)Ljava/lang/String;");
    jobject serviceResult = env->CallObjectMethod(service, loadMethod, userId);
    return serviceResult;
Run Code Online (Sandbox Code Playgroud)

笔记

  • 您可以缓存和重用JavaVM*jclassjmethodID整个应用程序。
  • JNIEnv*与线程相关联。它只能在一个线程内重用。
  • JNI 函数本身是线程安全的。但是,如果您在 C++ 中使用静态变量,请确保对这些变量的访问在 C++ 领域正确同步。