1

我将 JVM 嵌入到现有的 C++ 应用程序中,并且需要使用类注册本机 Java 函数的实现。

考虑这个带有本机函数的简单类:

class Native {
  static {
    System.out.println("Class 'Native' static initializer called.");
  }

  public native int f(int i);
}

在 JVM 内部,我正在运行 OSGi,这就是我需要使用 Java 代码(使用正确的类加载器)而不是从 JNI 加载它们的原因。但是,为了使这个示例简单,我省略了 OSGi。

我有这四种不同的 Java 方法来获取jclassC++ 中的值:

class Bridge {
  public Class<?> getNativeClass1() throws ClassNotFoundException {
    return getClass().getClassLoader().loadClass("org.example.Native");
  }

  public Class<?> getNativeClass2() throws ClassNotFoundException {
    return Class.forName("org.example.Native", false, getClass().getClassLoader());
  }

  public Class<?> getNativeClass3() throws ClassNotFoundException {
    final Class<?> clazz = getClass().getClassLoader()
                                     .loadClass("org.example.Native");
    clazz.getMethods();
    return clazz;
  }

  public Class<?> getNativeClass4() throws ClassNotFoundException {
    return Class.forName("org.example.Native", true, getClass().getClassLoader());
  }
}

要在 C++ 中注册本机函数实现,Native.f()我调用:

JNIEnv* env = ...
jclass clazz = ...; // Calling one of the four methods above.
JNINativeMethod nativeMethod = {
  (char*) "f",      // Method name 'f'.
  (char*)  "(I)I;", // Signature 'int --> int'.
  (void*) f         // Pointer to C++ implementation of function.
};
env->RegisterNatives(clazz, &nativeMethod, 1);

根据我用于获取Class<?>实例的方法,我得到不同的结果:

  • getNativeClass1()Native:加载类时(但当然是创建类的实例时),静态初始化程序不会在类中执行,并且本机实现正确绑定。(在 Java 中调用本机函数会产生不正确的结果或使 JVM 崩溃。)
  • getNativeClass2(): 同上。
  • getNativeClass3()Native:加载类时仍然没有在类中调用静态初始化程序,但本机实现正确绑定,我可以f()成功调用。
  • getNativeClass3():当类被加载并且本地实现正确绑定时,静态初始化器在类中被调用。Native

因此,似乎ClassLoader.loadClass()以某种方式加载了类,因此它没有正确初始化并且JNIEnv::RegisterNatives()无法正常工作。但是,调用Class.getMethods()将以某种方式初始化类(不调用静态初始化程序),以便绑定本机方法。

另一方面,Class.forName(clazz, false, classLoader)它的工作方式似乎与Class.loadClass()返回未初始化的Class实例完全一样。

任何人都可以解释两者之间的区别

  1. 一个未初始化的类,如 and 返回的getNativeClass1()getNativeClass2()
  2. 一个部分初始化的类,如返回的类getNativeClass3()
  3. 一个完全初始化的类,如返回的类getNativeClass4()

在调用之前加载类的最便携的方法是JNIEnv::RegisterNatives()什么?

4

1 回答 1

3

所以似乎 ClassLoader.loadClass() 以某种方式加载类,因此它没有正确初始化

根据文档

loadClass(String):“调用此方法等效于调用 loadClass(name, false)。”

loadClass(String,boolean)(添加了重点):“如果使用上述步骤找到了类,并且解析标志为 true,则此方法将在生成的 Class 对象上调用 resolveClass(Class) 方法”

这两种方法供需要在加载和链接之间做一些事情的类加载器内部使用。我不确定为什么loadClass(String)标记为公开,但可以说不应该。

在调用之前加载类的最便携的方法是什么

Class.forName(),它使用上下文类加载器并确保该类已准备好使用。

于 2013-01-14T15:08:24.167 回答