当前位置: 首页 > 面试题库 >

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

游高杰
2023-03-14
问题内容

我在IBM上读到

要访问Java对象的字段并调用其方法,本机代码必须调用FindClass(),GetFieldID(),GetMethodId()和GetStaticMethodID()。对于GetFieldID(),GetMethodID()和GetStaticMethodID(),对于给定类返回的ID在JVM进程的生存期内不会更改。但是,获取字段或方法的调用可能需要在JVM中进行大量工作,因为字段和方法可能是从超类继承的,从而使JVM遍历类层次结构来查找它们。因为给定类的ID是相同的,所以您应该先查找一次然后重用它们。同样,查找类对象可能很昂贵,因此也应该对其进行缓存。

一个人如何 缓存methodIDfieldID以及class在JNI对象?是否有必须遵循的内置方法或特定过程?


问题答案:

没有可遵循的内置方法,但是这里有一些标准,简洁和可重复的实现,显示了我如何实践IBM的建议。

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

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

void JNI_OnLoad(JavaVM * vm,void * reserved):
此方法将用于将类ID注册为全局变量,并将方法ID和字段ID分配给静态变量。当Java VM加载驱动程序时,将自动调用该方法。在驱动程序生命周期中仅调用一次。

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

原理: 我的理解是必须将类ID注册为全局引用,以维护任何关联的方法ID
/字段ID的可行性。如果不这样做,并且从JVM卸载了类,则在重新加载类时,方法ID
/字段ID可能会不同。如果将类ID注册为全局引用,则不需要将关联的方法ID和字段ID注册为全局引用。将类ID注册为全局引用可以防止关联的Java类卸载,因此可以稳定方法ID
/字段ID的值。包括类ID在内的全局引用应在中删除JNI_OnUnload()

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

样例代码

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

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

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 
     * instantiated Java classes.
     */
    public native void instanceNativeMethod(BeanObject bean);
}

这是使用javahon 生成的C ++头文件NativeClazz

/* 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;
static jmethodID JMID_BeanObject_getFoo;

/**************************************************************
 * 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);
}


 类似资料:
  • 我看了IBM 要访问Java对象的字段并调用它们的方法,本机代码必须调用FindClass()、GetFieldID()、GetMachodId()和GetStaticMachodID()。在GetFieldID()、GetMachodID()和GetStaticMachodID()的情况下,为给定类返回的ID在JVM进程的生命周期内不会改变。但是获取字段或方法的调用可能需要在JVM中进行大量工作

  • 我想跨调用缓存方法ID。为此,我得到方法ID: 我的问题是:我是否需要使用NewGlobalRef创建全局引用,或者可以从本地引用获取并保存方法ID(我使用FindClass获取class\u HelloWorld),而不需要全局引用类元数据?

  • 本文向大家介绍微信小程序实现缓存根据不同的id来进行设置和读取缓存,包括了微信小程序实现缓存根据不同的id来进行设置和读取缓存的使用技巧和注意事项,需要的朋友参考一下 本文是根据不同的id来进行设置和读取缓存,是同步缓存的方式: jonNums.count 是接口返回的数据  是报名总人数 newNumber  新的报名总人数 - 缓存上次的报名总人数 = 新增报名人数 感谢阅读,希望能帮助到大家

  • 嗨,为了保持向后兼容性,可以更改协议缓冲区中字段的数据类型吗。例如 旧消息 所以基本上我没有更改字段的标签号,也没有重命名它,但我更改了数据类型。那么这是向后兼容的吗?如果一个应用程序获得了一个用以前的模式创建的旧proto对象,它可以通过这个新模式创建的对象进行解析吗?

  • 问题内容: 假设使用Microsoft SQL Server 2008,有一个table1保留选定的省,地区,公社和村庄的ID。然后是表2,其中包含省,区,公社和村庄的ID和名称。省和地区是必填字段,并且始终会被填写。公社和村庄可能会被填补,但由于不需要,甚至可能不会被填补。 在不知道表1中是否填写公社和村庄的ID的情况下,构建动态SQL语句的最佳方法是什么。 如果未填写表1中的ID,此语句将给出

  • 问题内容: 在对fielddata与doc_values进行的一些实验中,我遇到了一个奇怪的情况。在我以前的映射中,我根本没有使用doc值。在新的映射中,我已添加到映射中的所有字段,但分析的字符串字段和布尔值(直到2.0才受支持)除外。 因此,详细而言,这是我的处理方式: 重新索引所有数据之前,我重新启动了ES 1.7集群,并使用排序,聚合和脚本字段运行查询以“预热”字段数据缓存。然后,我询问端点