JNI可能你看点Java的书或者JVM的书,都会提到,但是好像很多就是点到为止,也不具体说一下到底是啥,干什么的,真的是“不讲武德”。
JNI全程是Java Native Interface,一句话来说就是Java的本地接口,和我们常见的很多Java方法,很多实现是由Java本身来实现的,有的时候还需要本地方法来提供相关的功能。
说一句白话,就是我们平常一提到Java就说,“跨平台,可移植性强”,但是问题也来了,很多平台专门提供的能力有时候你就用不到了,A家有B家没有,你又想在A家平台用,也就说这些功能实际上还需要平台来提供。
JNI是什么
既然是JNI,I是interface接口的意思,那他和别的接口有啥区别?都是接口的话,我哪知道你是本地的还是外地的。还有就是JNI的实现方法在哪里?
区别:JNI对应的接口实际上就是我们看源码经常遇到的native修饰的方法,这样的方法也很独特,跟接口里面的方法一样,没有方法的具体实现,用native来修饰。
我用Object里的hashcode来举例子,这个hashcode可是以后对对象加锁的一个根据,不要小看他:
/**
* Returns a hash code value for the object. This method is
* supported for the benefit of hash tables such as those provided by
* {@link java.util.HashMap}.
* <p>
* The general contract of {@code hashCode} is:
* <ul>
* <li>Whenever it is invoked on the same object more than once during
* an execution of a Java application, the {@code hashCode} method
* must consistently return the same integer, provided no information
* used in {@code equals} comparisons on the object is modified.
* This integer need not remain consistent from one execution of an
* application to another execution of the same application.
* <li>If two objects are equal according to the {@code equals(Object)}
* method, then calling the {@code hashCode} method on each of
* the two objects must produce the same integer result.
* <li>It is <em>not</em> required that if two objects are unequal
* according to the {@link java.lang.Object#equals(java.lang.Object)}
* method, then calling the {@code hashCode} method on each of the
* two objects must produce distinct integer results. However, the
* programmer should be aware that producing distinct integer results
* for unequal objects may improve the performance of hash tables.
* </ul>
* <p>
* As much as is reasonably practical, the hashCode method defined by
* class {@code Object} does return distinct integers for distinct
* objects. (This is typically implemented by converting the internal
* address of the object into an integer, but this implementation
* technique is not required by the
* Java™ programming language.)
*
* @return a hash code value for this object.
* @see java.lang.Object#equals(java.lang.Object)
* @see java.lang.System#identityHashCode
*/
public native int hashCode();
代码只有一行,解释是他的几十倍。
JNI的实现:
实际上是由C或者C++语言来实现,为了和其它的方法实现做个对比,我先来一个JVM的图,Java方法栈针对的是Java的方法栈,而本地方法栈针对的就是针对本平台一些C实现的方法等的实现
JNI是怎么混进JVM的
那这个本地方法怎么混进JVM的本地方法栈的,毕竟JVM也有一套自己的加载机制,不是说你写一个方法JVM就会去加载。
所以JNI的具体实现的方法加载是怎么样的?实际上JNI的C语言方法加载大概分成两部分:“主动型”,“被动型”。这些都是我创造的词,这里的主动被动是JVM为主体来体现的。
主动的:JVM会根据native方法去找对应的C语言,JVM查找是根据一定命名规则来查找,毕竟Java和C是不同的语言,有不同的规范
查找的规则:native对法对应C函数一般是Java_开头,后面跟着包名和方法名(Java的native方法的包和类名),比如:
Java_org_example_Foo_foo
方法的写法格式不一样,C不支持的怎么办,比如说斜杠“\”之类的,那就把Java的“\”替换为"_",同理还有类似的替换的有:
Java的“_” 替换为C的"_1"
Java的“;” 替换为C的"_1"
Java的“[” 替换为C的"_1"
被动型:首先要有一个“主动”的C函数方法实现,被JVM加载,然后吊起的方法里面会有其他对应的native方法实现,于是这些方法没有按照规则命名,被动的加载进来。
比如说有一个方法叫做registerNatives方法,放在类里面的静态块里,加载的时候第一个加载,然后把里面的native方法都带上
这种“主动”的方法里面,例子如下:
// 注:Object类的registerNatives方法的实现位于java.base模块里的C代码中
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]));
}
你看methods这个就是正规Java的命名。
JNI的API
JNI也有API,这里是Oracle列出来的,也没去比较和OpenJDK的区别
JNI API
JNI和垃圾回收
实际上JNI也会生成对象,拿着对象会不会被回收?会但是一般来说不会轻易回收,JNI有局部引用和全部引用,不管是哪个,都会被JVM标为“不可回收”,只不过局部引用会在native方法返回失效,可以作为回收的对象
微信公众号:我是坑货