我看了IBM
要访问Java对象的字段并调用它们的方法,本机代码必须调用FindClass()、GetFieldID()、GetMachodId()和GetStaticMachodID()。在GetFieldID()、GetMachodID()和GetStaticMachodID()的情况下,为给定类返回的ID在JVM进程的生命周期内不会改变。但是获取字段或方法的调用可能需要在JVM中进行大量工作,因为字段和方法可能是从超类继承的,这使得JwalVM在类层次结构中向上查找它们。因为给定类的ID是相同的,所以您应该查找它们一次,然后重用它们。同样,查找类对象可能很昂贵,因此它们也应该被缓存。
如何在JNI中缓存methodID、fieldID和class对象?是否有必须遵循的内置方法或特定程序?
你可以有一些像这样的实用结构:
typedef struct MYVARIANT_FID_CACHE {
int cached;
jclass clazz;
jfieldID pAddress;
} MYVARIANT_FID_CACHE;
MYVARIANT_FID_CACHE VARIANTFc;
void cacheMYVARIANTFields(JNIEnv *env, jobject lpObject)
{
if (VARIANTFc.cached) return;
VARIANTFc.clazz = env->GetObjectClass(lpObject);
VARIANTFc.pAddress = env->GetFieldID(VARIANTFc.clazz, "pAddress", "I");
VARIANTFc.cached = 1;
}
VARIANT *getMYVARIANTFields(JNIEnv *env, jobject lpObject, VARIANT *lpStruct)
{
if (!VARIANTFc.cached) cacheVARIANT2Fields(env, lpObject);
lpStruct = (VARIANT*)(env->GetIntField(lpObject, VARIANTFc.pAddress));
return lpStruct;
}
这是摘自我的问题:https://stackoverflow.com/questions/10617714/how-to-extend-swt-com-support
有关一些好的示例,请查看os_structs. c
,它与eclipse SWT实现捆绑在一起。
注意:以上代码只是一个示例,可以适用于不同的操作系统。此外,它只是展示了“如何访问java字段”;对于方法,您可以遵循相同的方法。
下面是我如何实践IBM的建议。考虑到这样的演示java类:
public class SimpleClazz {
public int value = 10;
public native int getValue();
static {
// Load Native Library
System.loadLibrary("The native library name");
}
}
相应的jni头文件如下:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class SimpleClazz */
#ifndef _Included_SimpleClazz
#define _Included_SimpleClazz
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: SimpleClazz
* Method: getValue
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_SimpleClazz_getValue
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
根据IBM的建议,我们需要缓存使用的类SimpleClazz和对象成员值的字段id。
在学习了这篇好文章之后,我将SimpleClazz缓存在函数JNI\u OnLoad中,该函数在加载本机库时调用(例如,通过System.loadLibrary)。在JNI_Onload中,我们找到类并将这个jclass存储为全局字段。
再者,在getValue
的本机实现中,我们使用静态局部变量来缓存value
的字段id。这种设计是为了确保这个归档的id可以在更好的范围内,而不是在全局范围内。这种设计的缺点是我们每次调用这个函数时都需要与NULL进行比较。我从《Java本机接口:程序员指南和规范》一书的第4.4.1节中学习了这种设计。
最后,我们还需要编写函数JNI_OnUnload
,当包含本机库的类加载器被垃圾回收时调用,在这个函数中,我们释放了jclass的全局引用。
我的cpp实现如下所示:
#include <jni.h>
#include <SimpleClazz.h>
static jclass simpleCls;
// According to http://docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/invocation.html#JNI_OnLoad
// The VM calls JNI_OnLoad when the native library is loaded (for example, through System.loadLibrary).
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
JNIEnv* env;
if ((*vm)->GetEnv(vm, (void **) &env, JNI_VERSION_1_6) != JNI_OK) {
return JNI_ERR;
} else {
jclass localSimpleCls = (*env)->FindClass("SimpleClazz");
if (localSimpleCls == NULL) {
return JNI_ERR;
}
simpleCls = (jclass) (*env)->NewGlobalRef(env, localSimpleCls);
}
return JNI_VERSION_1_6;
}
JNIEXPORT jint JNICALL Java_SimpleClazz_getValue(JNIEnv * env, jobject thiz){
static jfieldID valueID = NULL;
if (valueID == NULL) {
valueID = (*env)->GetFieldID(env, simpleCls, "value", "I");
if (valueID == NULL){
return JNI_ERR; // Exception thrown
}
}
jint value = (*env)->GetIntField(env, thiz, valueID);
return value;
}
// According to http://docs.oracle.com/javase/1.5.0/docs/guide/jni/spec/invocation.html#JNI_OnUnload
// The VM calls JNI_OnUnload when the class loader containing the native library is garbage collected.
void JNI_OnUnload(JavaVM *vm, void *reserved) {
JNIEnv* env;
if ((*vm)->GetEnv(vm, (void **) &env, JNI_VERSION_1_6) != JNI_OK) {
// Something is wrong but nothing we can do about this :(
return;
} else {
if (0 != NULL){
(*env)->DeleteGlobalRef(env, simpleCls);
}
}
}
没有内置的方法可以遵循,但是这里有一个标准的、干净的、可重复的实现,展示了我如何实践IBM的建议。
我假设您正在从Java调用DLL,并且在应用程序生命周期中多次引用它。
示例本机Java类名为org。堆栈溢出。jni。NativeClazz将实现两种内置JNI方法:JNI\u OnLoad()和JNI\u onnload()。
void JNI\u OnLoad(JavaVM*vm,void*reserved)
:此方法将用于将类ID注册为全局变量,并将方法ID和字段ID分配给静态变量。该方法在Java虚拟机加载驱动程序时自动调用;在驾驶员生命周期内只调用一次。
JNI_OnUnload:此方法将用于释放任何全局变量注册的JNI_OnLoad()
。VM将自动调用JNI_OnUnload()
之前立即应用程序关闭。
理由:据我所知,类ID必须注册为全局引用,以保持任何相关方法ID/字段ID的可行性。如果不这样做,并且从JVM卸载类,则在重新加载类时,方法ID/字段ID可能不同。如果类ID注册为全局引用,则不需要将关联的方法ID和字段ID注册为全局引用。将类ID注册为全局引用可以防止卸载关联的Java类,从而稳定方法ID/字段ID值。全局引用,包括类ID,应该在JNI\u OnUnload()中删除。
方法ID和字段ID不受本机代码管理;它们由虚拟机管理,在卸载相关类之前有效。在虚拟机卸载定义类之前,不能显式删除字段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\u stackoverflow\u jni\u NativeClazz.h”:
/* 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);
}
问题内容: 我在IBM上读到 要访问Java对象的字段并调用其方法,本机代码必须调用FindClass(),GetFieldID(),GetMethodId()和GetStaticMethodID()。对于GetFieldID(),GetMethodID()和GetStaticMethodID(),对于给定类返回的ID在JVM进程的生存期内不会更改。但是,获取字段或方法的调用可能需要在JVM中进行
我想跨调用缓存方法ID。为此,我得到方法ID: 我的问题是:我是否需要使用NewGlobalRef创建全局引用,或者可以从本地引用获取并保存方法ID(我使用FindClass获取class\u HelloWorld),而不需要全局引用类元数据?
嗨,为了保持向后兼容性,可以更改协议缓冲区中字段的数据类型吗。例如 旧消息 所以基本上我没有更改字段的标签号,也没有重命名它,但我更改了数据类型。那么这是向后兼容的吗?如果一个应用程序获得了一个用以前的模式创建的旧proto对象,它可以通过这个新模式创建的对象进行解析吗?
问题内容: 在对fielddata与doc_values进行的一些实验中,我遇到了一个奇怪的情况。在我以前的映射中,我根本没有使用doc值。在新的映射中,我已添加到映射中的所有字段,但分析的字符串字段和布尔值(直到2.0才受支持)除外。 因此,详细而言,这是我的处理方式: 重新索引所有数据之前,我重新启动了ES 1.7集群,并使用排序,聚合和脚本字段运行查询以“预热”字段数据缓存。然后,我询问端点
我试图在JavaJNI中缓存方法ID和JClass,但是当我使用缓存的值时,我遇到了EXE_BAD_ACCESS。当我请求与使用它们的函数内联的值时,错误消失了。我发现我需要使用全局引用,但是这并没有解决分段错误。 jni缓存的相关信息(稍微过时的签名)-在JNI中,如何根据IBM的性能建议缓存类、方法ID和字段ID? 标题: CPP: 用法(不同的CPP类,导入共享头): 后果 新类:0x7fc
在本章中,我们将了解Grav中的性能和缓存概念。 表现(Performance) 术语“性能”指的是系统性能,使其能够处理更高的系统负载并修改系统以处理更高的负载。 考虑以下与Grav表现有关的要点 - 要获得更好的opcache性能,可以使用PHP opcache和usercache 。 opcache适用于PHP 5.4,使用PHP 5.5,PHP 5.6和Zend opcache可以更快地运