java - 在 JNI 中,如何根据 IBM 的性能建议缓存类、方法 ID 和字段 ID?

标签 java performance caching java-native-interface

我在 on IBM 上阅读那个

To access Java objects' fields and invoke their methods, native code must make calls to FindClass(), GetFieldID(), GetMethodId(), and GetStaticMethodID(). In the case of GetFieldID(), GetMethodID(), and GetStaticMethodID(), the IDs returned for a given class don't change for the lifetime of the JVM process. But the call to get the field or method can require significant work in the JVM, because fields and methods might have been inherited from superclasses, making the JVM walk up the class hierarchy to find them. Because the IDs are the same for a given class, you should look them up once and then reuse them. Similarly, looking up class objects can be expensive, so they should be cached as well.

如何缓存 JNI 中的methodIDfieldIDclass 对象? 是否有必须遵循的内置方法或特定程序?

最佳答案

没有可遵循的内置方法,但这里有一个标准、干净且可重复的实现,展示了我如何实践 IBM 的建议。

我将假设您从 Java 调用 DLL,并且在整个应用程序生命周期中多次引用它。

示例 Native Java 类名为 org.stackoverflow.jni.NativeClazz,它将实现 2 个内置 JNI 方法 JNI_OnLoad()JNI_OnUnload ().

void JNI_OnLoad(JavaVM *vm, void *reserved):该方法将用于将Class IDs注册为全局变量,并将Method IDs和Field IDs分配给静态变量。该方法在Java VM加载驱动时自动调用;它在驱动程序生命周期中只被调用一次。

void JNI_OnUnload(JavaVM *vm, void *reserved):此方法将用于释放 JNI_OnLoad() 注册的所有全局变量。 VM 将在应用程序关闭前立即自动调用 JNI_OnUnload()

理由:据我了解,必须将类 ID 注册为全局引用,以保持任何关联的方法 ID/字段 ID 的可行性。如果没有这样做并且类从 JVM 中卸载,则在类重新加载时,方法 ID/字段 ID 可能不同。如果 Class ID 注册为全局引用,则关联的 Method IDs 和 Field IDs 不需要注册为全局引用。将类 ID 注册为全局引用可防止卸载关联的 Java 类,从而稳定方法 ID/字段 ID 值。应在 JNI_OnUnload() 中删除全局引用,包括 Class ID。

方法 ID 和字段 ID 不由 native 代码管理;它们由虚拟机管理并且在关联类被卸载之前一直有效。在虚拟机卸载定义类之前,不能显式删除字段 ID 和方法 ID;它们可以在卸载后留给 VM 处理。

示例代码

以下 C++ 代码部分中的注释解释了全局注册变量。

这是代表数据对象的 Java 类 BeanObject:

package org.stackoverflow.data;

public class BeanObject {

    String foo = "";
    
    public String getFoo() {
     
        return foo;
    }
}

这里是一个框架Java类NativeClazz:

package org.stackoverflow.jni;

import org.stackoverflow.data.BeanObject;

public class NativeClazz {

    // Static area for forced initialization
    static {

        // Load Native Library (C++); calls JNI_OnLoad()
        System.loadLibrary("Native_Library_File_Name");
    }       

    /**
     * A static native method you plan to call.
     */
    public static native void staticNativeMethod(BeanObject bean);

    /**
     * A non-static native method you plan to call, to show this also works with 
     * Java class instances.
     */
    public native void instanceNativeMethod(BeanObject bean);
}

这是在 NativeClazz 上使用 javah 生成的 C++ 头文件“org_stackoverflow_jni_NativeClazz.h”: p>

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class org_stackoverflow_jni_NativeClazz */

#ifndef _Included_org_stackoverflow_jni_NativeClazz
#define _Included_org_stackoverflow_jni_NativeClazz
#ifdef __cplusplus
extern "C" {
#endif

/*
 * Class:     org_stackoverflow_jni_NativeClazz_staticNativeMethod
 * Method:    staticNativeMethod
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_org_stackoverflow_jni_NativeClazz_staticNativeMethod
  (JNIEnv *, jclass, jobject);

/*
 * Class:     org_stackoverflow_jni_NativeClazz_instanceNativeMethod
 * Method:    instanceNativeMethod
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_org_stackoverflow_jni_NativeClazz_instanceNativeMethod
  (JNIEnv *, jobject, jobject);

#ifdef __cplusplus
}
#endif
#endif

这里是实现头文件的 C++ .cpp 文件:

#include "org_stackoverflow_jni_NativeClazz.h"

using namespace std;

/**************************************************************
 * Static Global Variables to cache Java Class and Method IDs
 **************************************************************/
static jclass JC_BeanObject;              // declare for each class
static jmethodID JMID_BeanObject_getFoo;  // declare for each class method

/**************************************************************
 * Declare JNI_VERSION for use in JNI_Onload/JNI_OnUnLoad
 * Change value if a Java upgrade requires it (prior: JNI_VERSION_1_6)
 **************************************************************/
static jint JNI_VERSION = JNI_VERSION_1_8;

/**************************************************************
 * Initialize the static Class and Method Id variables
 **************************************************************/
jint JNI_OnLoad(JavaVM* vm, void* reserved) {

    // Obtain the JNIEnv from the VM and confirm JNI_VERSION
    JNIEnv* env;
    if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION) != JNI_OK) {

        return JNI_ERR;
    }

    // Temporary local reference holder
    jclass tempLocalClassRef;

    // STEP 1/3 : Load the class id
    tempLocalClassRef = env->FindClass("org/stackoverflow/data/BeanObject");

    // STEP 2/3 : Assign the ClassId as a Global Reference
    JC_BeanObject = (jclass) env->NewGlobalRef(tempLocalClassRef);

    // STEP 3/3 : Delete the no longer needed local reference
    env->DeleteLocalRef(tempLocalClassRef);
    
    // Load the method id
    JMID_BeanObject_getFoo = env->GetMethodID(JC_BeanObject, "getFoo", "(Ljava/lang/String;)V");

    // ... repeat prior line for any other methods of BeanObject

    // ... repeat STEPS 1-3 for any other classes; re-use tempLocalClassRef.

    // Return the JNI Version as required by method
    return JNI_VERSION;
}

/**************************************************************
 * Destroy the global static Class Id variables
 **************************************************************/
void JNI_OnUnload(JavaVM *vm, void *reserved) {

    // Obtain the JNIEnv from the VM
    // NOTE: some re-do the JNI Version check here, but I find that redundant
    JNIEnv* env;
    vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION);

    // Destroy the global references
    env->DeleteGlobalRef(JC_BeanObject);
    
    // ... repeat for any other global references
}

/**************************************************************
 * A Static Native Method
 **************************************************************/
JNIEXPORT void JNICALL Java_org_stackoverflow_jni_NativeClazz_staticNativeMethod
               (JNIEnv * env, jclass clazz, jobject jBeanObject) {
    
    // Retrieve jstring from the Java Object
    jstring jFoo = (jstring)env->CallObjectMethod(jBeanObject, JMID_BeanObject_getFoo);

    // Make accessible to C++
    const char * cFoo = env->GetStringUTFChars(jFoo, NULL);             

    // Do something with cFoo...

    // Release Resources
    env->ReleaseStringUTFChars(jFoo, cFoo);
    env->DeleteLocalRef(jFoo);
}

/**************************************************************
 * Instance / Non-Static Native Method
 **************************************************************/
JNIEXPORT void JNICALL Java_org_stackoverflow_jni_NativeClazz_instanceNativeMethod
               (JNIEnv * env, jobject selfReference, jobject jBeanObject) {

    // Retrieve jstring from the Java Object
    jstring jFoo = (jstring)env->CallObjectMethod(jBeanObject, JMID_BeanObject_getFoo);               

    // Make accessible to C++
    const char * cFoo = env->GetStringUTFChars(jFoo, NULL);             

    // Do something with cFoo...

    // Release Resources
    env->ReleaseStringUTFChars(jFoo, cFoo);
    env->DeleteLocalRef(jFoo);
}

关于java - 在 JNI 中,如何根据 IBM 的性能建议缓存类、方法 ID 和字段 ID?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10617735/

相关文章:

java - Intellij Idea 无法启动(单击应用程序绝对不会执行任何操作)

c# - 室内Wi-Fi定位API?

python - 请求与请求 future - 响应时间不准确?

ios - Swift 数据缓存 - 最佳实践

caching - 启动时初始化 Java EE 应用程序缓存

java - 要存储在 hazelcast 中用于缓存的 IMap 中的数据存储在 hazelcast 服务器中还是存储在 hazelcast 实例运行的位置?

.net - linq to sql将这个查询翻译成: "select *"?格式是不是效率低下

performance - Propel 2 和 Doctrine 2 有详细的比较吗

ruby-on-rails - 我可以在 Rails 3 中以编程方式更改 config.cache_classes 吗?

java - 在同步关系之前发生了什么?什么是过早的对象泄漏?