jni是java调用native代码的一种机制。
例如我们写一个java类,里面用到native
方法add
package com.jni;
public class JNIDemo {
private native int add(int a, int b);
static {
System.loadLibrary("MyJNI");
}
public static void main(String[] args) {
int s = new JNIDemo().add(1, 1);
System.out.println(s);
}
}
然后生成h文件
# 如果有javah则也可以直接用javah指令
$ javac -h . com/jni/JNIDemo.java
到此得到以下文件目录
打开h文件看一下内容如下,就是定义了一个Java_com_jni_JNIDemo_add
方法,该方法签名格式是固定的,我们不需要管他。
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_jni_JNIDemo */
#ifndef _Included_com_jni_JNIDemo
#define _Included_com_jni_JNIDemo
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_jni_JNIDemo
* Method: add
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_com_jni_JNIDemo_add
(JNIEnv *, jobject, jint, jint);
#ifdef __cplusplus
}
#endif
#endif
接下来实现h文件中的方法,写一个demo.c
文件如下
#include <jni.h>
#include "com_jni_JNIDemo.h"
// JNIEXPORT是一个宏定义用来输入JNI格式, jint是返回值类型,是java中的int
// JNICALL:也是一个宏定义,用于指定函数调用约定,函数名Java_<全限定类名>_<方法名>
// 参数前2个分别是jni的环境,java中this对象和两个入参jint类型的a,b
JNIEXPORT jint JNICALL Java_com_jni_JNIDemo_add
(JNIEnv * env, jobject instance, jint a, jint b) {
return a + b;
}
然后通过gcc将文件编译成动态链接库,这里以linux为例,注意so文件名一定是lib
开头,后面的MyJNI
与java中System.loadLibrary("MyJNI");
部分保持一致。编译完之后,需要把so文件扔到/usr/java/packages/lib, /usr/lib64, /lib64, /lib, /usr/lib
这几个目录之一,否则加载时找不到。
$ gcc -I"$JAVA_HOME/include" -I"$JAVA_HOME/include/linux" -shared -o libMyJNI.so demo.c
$ cp libMyJNI.so /usr/lib
当然也可以在java中通过绝对路径的方式制定so文件
System.loadLibrary("/path/to/so/xx.so");
JVMTI提供了一些对于jvm的监控性的api,也需要在jni下才能调用,即引入jvmti.h
头文件,即可使用jvmti。
例如写一个查看jvm中某一个Class
的instance列表(最多取10个避免oom)。
package com.jvmti;
import java.util.*;
public class JVMTIDemo {
static { System.loadLibrary("MyJVMTI");}
private native Object[] getInstance(Class cls);
public static void main(String[] args) {
List<A> list= new ArrayList<>();
for (int i = 0; i < 10; i++) {
A a = new A();
System.out.println(a.hashCode());
list.add(a);
}
for(Object obj : new JVMTIDemo().getInstance(A.class)) {
System.out.println(obj.hashCode());
}
}
}
class A {}
此时用javac -h . com/jvmti/JVMTIDemo.java
得到的头文件com_jvmti_JVMTIDemo.h
如下
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_jvmti_JVMTIDemo */
#ifndef _Included_com_jvmti_JVMTIDemo
#define _Included_com_jvmti_JVMTIDemo
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_jvmti_JVMTIDemo
* Method: getInstance
* Signature: (Ljava/lang/Class;)[Ljava/lang/Object;
*/
JNIEXPORT jobjectArray JNICALL Java_com_jvmti_JVMTIDemo_getInstance
(JNIEnv *, jobject, jclass);
#ifdef __cplusplus
}
#endif
#endif
接下来写个MyJVMTI.c
文件如下
#include <jni.h>
#include <jvmti.h>
#include <string.h>
#include "com_jvmti_JVMTIDemo.h"
static jvmtiIterationControl JNICALL tagInstance(jlong class_tag, jlong size, jlong* tag_ptr, void* user_data) {
int* totalCount = (int*)user_data;
if (*totalCount > 100) {
return JVMTI_ITERATION_ABORT;
}
(*totalCount)++;
*tag_ptr = 1;
return JVMTI_ITERATION_CONTINUE;
}
JNIEXPORT jobjectArray JNICALL Java_com_jvmti_JVMTIDemo_getInstance
(JNIEnv* env, jobject instance, jclass cls) {
jvmtiEnv* jvmti;
JavaVM* vm;
(*env)->GetJavaVM(env, &vm);
(*vm)->GetEnv(vm, (void**)&jvmti, JVMTI_VERSION_1_0);
jvmtiCapabilities capabilities;
memset(&capabilities, 0, sizeof(jvmtiCapabilities));
capabilities.can_tag_objects = 1;
(*jvmti)->AddCapabilities(jvmti, &capabilities);
int totalCount = 0;
jlong tag = 1;
jint count;
jobject* instances;
// 遍历所有class类的instance,最多遍历十个对象
(*jvmti)->IterateOverInstancesOfClass(jvmti, cls, JVMTI_HEAP_OBJECT_EITHER, &tagInstance, &totalCount);
(*jvmti)->GetObjectsWithTags(jvmti, 1, &tag, &count, &instances, NULL);
// count 应该等于 totalCount
jobjectArray result = (*env)->NewObjectArray(env, totalCount, cls, NULL);
for (int i = 0; i < count; i++) {
(*env)->SetObjectArrayElement(env, result, i, instances[i]);
(*jvmti)->SetTag(jvmti, instances[i], 0);
}
(*jvmti)->Deallocate(jvmti, (unsigned char*)instances);
return result;
}
// 下面是c++版本的
// #include <jni.h>
// #include <jvmti.h>
// #include "com_jvmti_JVMTIDemo.h"
// static jvmtiIterationControl JNICALL tagInstance(jlong class_tag, jlong size, jlong* tag_ptr, void* user_data) {
// int* totalCount = (int *)user_data;
// if (*totalCount > 100) {
// return JVMTI_ITERATION_ABORT;
// }
// (*totalCount) ++;
// *tag_ptr = 1;
// return JVMTI_ITERATION_CONTINUE;
// }
// JNIEXPORT jobjectArray JNICALL Java_com_jvmti_JVMTIDemo_getInstance
// (JNIEnv* env, jobject instance, jclass cls) {
// jvmtiEnv *jvmti;
// JavaVM* vm;
// env->GetJavaVM(&vm);
// vm->GetEnv((void**)&jvmti, JVMTI_VERSION_1_0);
// jvmtiCapabilities capabilities = {0};
// capabilities.can_tag_objects = 1;
// jvmti->AddCapabilities(&capabilities);
// int totalCount = 0;
// jlong tag = 1;
// jint count;
// jobject* instances;
// // 遍历所有class类的instance,最多遍历十个对象
// jvmti->IterateOverInstancesOfClass(cls, JVMTI_HEAP_OBJECT_EITHER, &tagInstance, &totalCount);
// jvmti->GetObjectsWithTags(1, &tag, &count, &instances, NULL);
// // count 应该等于 totalCount
// jobjectArray result = env->NewObjectArray(totalCount, cls, NULL);
// for (int i = 0; i < count; i++) {
// env->SetObjectArrayElement(result, i, instances[i]);
// jvmti->SetTag(&instance[i], 0);
// }
// jvmti->Deallocate((unsigned char*)instances);
// return result;
// }
通过gcc或clang编译成so文件
$ gcc -I"$JAVA_HOME/include" -I"$JAVA_HOME/include/linux" -shared -o libMyJVMTI.so MyJVMTI.c
$ clang -I"$JAVA_HOME/include" -I"$JAVA_HOME/include/linux" -shared -o libMyJVMTI.so MyJVMTI.c
$ sudo cp libMyJVMTI.so /usr/lib
$ java com.jvmti.JVMTIDemo
1993134103
604107971
123961122
1993134103
604107971
123961122