public class Object {
public native int hashCode();
}
当Java代码调用native方法时,JVM将通过 JNI ,调用至对应的C函数
Object.hashCode()就是一个native方法,对应的C函数将计算对象的哈希值,并缓存在 对象头 、 栈上锁记录 (轻量级锁)或者 对象监视锁 (重量级锁,monitor)中,以确保该值在 对象的生命周期之内不会变更
在调用native方法之前,JVM需要将该native方法链接至对应的C函数上
JVM自动查找符合 默认命名规范 的C函数,并且链接起来
package me.zhongmingmao.advanced.jni;
public class Foo {
int i = 0xDEADBEEF;
public static native void foo();
public native void bar(int i, long j);
public native void bar(String s, Object o);
}
$ javac -h . me/zhongmingmao/advanced/jni/Foo.java
$ cat me_zhongmingmao_advanced_jni_Foo.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class me_zhongmingmao_advanced_jni_Foo */
#ifndef _Included_me_zhongmingmao_advanced_jni_Foo
#define _Included_me_zhongmingmao_advanced_jni_Foo
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: me_zhongmingmao_advanced_jni_Foo
* Method: foo
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_me_zhongmingmao_advanced_jni_Foo_foo
(JNIEnv *, jclass);
/*
* Class: me_zhongmingmao_advanced_jni_Foo
* Method: bar
* Signature: (IJ)V
*/
JNIEXPORT void JNICALL Java_me_zhongmingmao_advanced_jni_Foo_bar__IJ
(JNIEnv *, jobject, jint, jlong);
/*
* Class: me_zhongmingmao_advanced_jni_Foo
* Method: bar
* Signature: (Ljava/lang/String;Ljava/lang/Object;)V
*/
JNIEXPORT void JNICALL Java_me_zhongmingmao_advanced_jni_Foo_bar__Ljava_lang_String_2Ljava_lang_Object_2
(JNIEnv *, jobject, jstring, jobject);
#ifdef __cplusplus
}
#endif
#endif
这种链接方式对C函数名没有要求,通常会使用一个名为 registerNatives
的native方法,该方法还是会按照 自动链接
的方式链接到对应的C函数,然后在 registerNatives
对应的C函数中, 手动链接该类的其他native方法
public class Object {
// 自动链接
private static native void registerNatives();
static {
registerNatives();
}
public final native Class<?> getClass();
// 主动链接
public native int hashCode();
public final native void wait(long timeout) throws InterruptedException;
public final native void notify();
public final native void notifyAll();
protected native Object clone() throws CloneNotSupportedException;
}
static JNINativeMethod methods[] = {
{"hashCode", "()I", (void *)&JVM_IHashCode},
{"wait", "(J)V", (void *)&JVM_MonitorWait},
{"notify", "()V", (void *)&JVM_MonitorNotify},
{"notifyAll", "()V", (void *)&JVM_MonitorNotifyAll},
{"clone", "()Ljava/lang/Object;", (void *)&JVM_Clone},
};
JNIEXPORT void JNICALL
Java_java_lang_Object_registerNatives(JNIEnv *env, jclass cls)
{
(*env)->RegisterNatives(env, cls,
methods, sizeof(methods)/sizeof(methods[0]));
}
JNIEXPORT jclass JNICALL
Java_java_lang_Object_getClass(JNIEnv *env, jobject this)
{
if (this == NULL) {
JNU_ThrowNullPointerException(env, NULL);
return 0;
} else {
return (*env)->GetObjectClass(env, this);
}
}
C函数将调用 RegisterNatives API ,注册Object类中其他native方法(不包括getClass)所要链接的C函数,这些C函数的函数名并 不符合默认的命名规则 ,详细的C代码请查阅 Object.c
// foo.c
#include <stdio.h>
#include "me_zhongmingmao_advanced_jni_Foo.h"
JNIEXPORT void JNICALL Java_me_zhongmingmao_advanced_jni_Foo_bar__Ljava_lang_String_2Ljava_lang_Object_2
(JNIEnv *env, jobject thisObject, jstring str, jobject obj) {
printf("Hello, World/n");
return;
}
通过 gcc 命令将其编译成 动态链接库 ,动态链接库的名字必须以 lib 为前缀,以 .dylib (Linux上为 .so )为扩展名
$ gcc -I$JAVA_HOME/include -I$JAVA_HOME/include/darwin -o libfoo.dylib -shared foo.c
// -Djava.library.path=$PATH_TO_DYLIB
public static void main(String[] args) {
try {
System.loadLibrary("foo");
} catch (UnsatisfiedLinkError e) {
e.printStackTrace();
System.exit(1);
}
new Foo().bar("", "");
}
$ java -Djava.library.path=$PATH_TO_DYLIB me.zhongmingmao.advanced.jni.Foo Hello, World
JNI会将Java层面的 基本类型 以及 引用类型 映射为另一套可供C代码使用的 数据结构
Java类型 C数据结构 -------------------- boolean jboolean byte jbyte char jchar short jshort int jint long jlong float jfloat double jdouble void jvoid
引用类型对应的数据结构之间也存在 继承 关系
jobject |- jclass (java.lang.Class objects) |- jstring (java.lang.String objects) |- jthrowable (java.lang.Throwable objects) |- jarray (arrays) |- jobjectArray (object arrays) |- jbooleanArray (boolean arrays) |- jbyteArray (byte arrays) |- jcharArray (char arrays) |- jshortArray (short arrays) |- jintArray (int arrays) |- jlongArray (long arrays) |- jfloatArray (float arrays) |- jdoubleArray (double arrays)
/* * Class: me_zhongmingmao_advanced_jni_Foo * Method: foo * Signature: ()V */ JNIEXPORT void JNICALL Java_me_zhongmingmao_advanced_jni_Foo_foo (JNIEnv *, jclass); /* * Class: me_zhongmingmao_advanced_jni_Foo * Method: bar * Signature: (IJ)V */ JNIEXPORT void JNICALL Java_me_zhongmingmao_advanced_jni_Foo_bar__IJ (JNIEnv *, jobject, jint, jlong); /* * Class: me_zhongmingmao_advanced_jni_Foo * Method: bar * Signature: (Ljava/lang/String;Ljava/lang/Object;)V */ JNIEXPORT void JNICALL Java_me_zhongmingmao_advanced_jni_Foo_bar__Ljava_lang_String_2Ljava_lang_Object_2 (JNIEnv *, jobject, jstring, jobject);
修改C代码,获取Foo类实例的i字段
JNIEXPORT void JNICALL Java_me_zhongmingmao_advanced_jni_Foo_bar__Ljava_lang_String_2Ljava_lang_Object_2
(JNIEnv *env, jobject thisObject, jstring str, jobject obj) {
// JNI中访问实例字段的方式类似于JAVA的反射API
jclass cls = (*env)->GetObjectClass(env, thisObject);
jfieldID fieldID = (*env)->GetFieldID(env, cls, "i", "I");
jint value = (*env)->GetIntField(env, thisObject, fieldID);
printf("Hello, World 0x%x/n", value);
return;
}
$ java -Djava.library.path=$PATH_TO_DYLIB me.zhongmingmao.advanced.jni.Foo Hello, World 0xdeadbeef
如果尝试获取 不存在 的实例字段j,会抛出异常
$ java -Djava.library.path=$PATH_TO_DYLIB me.zhongmingmao.advanced.jni.Foo
Hello, World 0x1
Exception in thread "main" java.lang.NoSuchFieldError: j
at me.zhongmingmao.advanced.jni.Foo.bar(Native Method)
at me.zhongmingmao.advanced.jni.Foo.main(Foo.java:19)
JNIEXPORT void JNICALL Java_me_zhongmingmao_advanced_jni_Foo_bar__Ljava_lang_String_2Ljava_lang_Object_2
(JNIEnv *env, jobject thisObject, jstring str, jobject obj) {
// JNI中访问实例字段的方式类似于JAVA的反射API
jclass cls = (*env)->GetObjectClass(env, thisObject);
jfieldID fieldID = (*env)->GetFieldID(env, cls, "j", "I");
if((*env)->ExceptionOccurred(env)) {
printf("Exception!/n");
(*env)->ExceptionClear(env);
}
fieldID = (*env)->GetFieldID(env, cls, "i", "I");
jint value = (*env)->GetIntField(env, thisObject, fieldID);
// we should put an exception guard here as well.
printf("Hello, World 0x%x/n", value);
return;
}
$ java -Djava.library.path=$PATH_TO_DYLIB me.zhongmingmao.advanced.jni.Foo Exception! Hello, World 0xdeadbeef
转载请注明出处:http://zhongmingmao.me/2019/01/12/jvm-advanced-jni/
访问原文「JVM进阶 -- 浅谈JNI」获取最佳阅读体验并参与讨论