jni java native interface

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

到此得到以下文件目录

image

打开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

image

当然也可以在java中通过绝对路径的方式制定so文件

System.loadLibrary("/path/to/so/xx.so");

JVMTI jvm tool interface

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