jni
名词解释:
JNI是什么:JNI是java Native Interface的缩写,提供了若干API实现了Java和其他语言的通信(主要是C&C++)。
为什么要用JNI:在C/C++中写的程序可以避开JVM的内存开销过大的限制、处理高性能的计算、调用系统服务(例如驱动)等功能。
JVM: jvm是java虚拟机在jni层的代表,全局只有一个。
JNIENV: 代表了java在本线程的运行环境,每个线程都有一个。
JOBJECT: 在JNI中除了基本类型数组、Class、String和throwable外其余所有Java对象的数据类型在JNI中都用jobject表示
JNI工作原理:
如图:
1. java层调用system.load方法。
2. 通过classloader拿到了so的绝对路径,然后调用nativeload()方法。
3. 通过linux下的dlopen方法,加载并查找so库里的方法,如有jni_onload会优先加载。
4. 当前线程下的jnienv会将所有的jni方法注册到了同一个vm中,so和class到了同一个进程空间。
5. 通过当前线程的jnienv即可调用对应的对象方法了。
为什么要在JNI层缓存ID:
先看一张native方法和java方法的调用同样方法的耗时统计图,可以看到,2者耗时差距为11倍。
设备 | native首次 | native 1万次 | java首次 | java一万次 |
荣耀3X | 0.04 | 10.56 | 0.02 | 1.17 |
NOTE3 | 0.03 | 3.02 | 0.009 | 0.04 |
NOTE4 | 0.07 | 3.56 | 0.06 | 0.19 |
0.07 | 3.44 | 0.02 | 0.35 | |
NEXUS6 | 0.20 | 4.12 | 0.03 | 0.40 |
平均 | 0.082 | 4.96 | 0.027 | 0.43 |
倍数 | 3.0 | 11.5 |
原因:
C++调用java需要查找类,查找方法,查找方法ID,获取字段或者方法的调用有时候会需要在JVM中完成大量工作,因为字段和方法可能是从超类中继承而来的,为特定类返回的id不会在Jvm进程生存期间发生变化 ,这会让jvm向上遍历类层次结构来找到它们,这是个开销很大的操作。
所以,缓存ID字段是为了降低cpu负载,提高运行速度,节约电量。
JNI里的缓存类型:
Global Reference: 全局引用生存周期为创建后,直到程序员显示的释放它,否则一直存在。
全局引用可以在多线程之间共享其指向的对象。
local Reference : 局部引用生存周期为创建后,直到DeleteLocalRef . 或在该方法结束后没有被JVM发现有JAVA层引用而被JVM回收并释放。
局部引用只对当前线程有效,多个线程间不能共享局部引用。
注意:
基于谁创建谁销毁的原则,native函数执行完后,局部引用没有被native代码显示删除,那么局部引用在JVM中还是有效的,JVM决定什么时候删除它,和C语言的局部变量含义是不一样的。
局部引用在JVM中是有个数限制的,默认16个,注意管理释放。
Weak Global Reference :
弱全局引用生命周期为创建之后,直到DeleteGlobalRef。或在内存紧张时进行回收而被释放。
注意:
使用弱全局变量的时候,要时刻记着:它所指向的对象可能已经被垃圾回收了。可通过静态变量和全局变量来保持弱全局引用。
缓存方法推荐:
jobject默认是local Ref,函数环境消失时会跟随消失
在jni_onload初始化全局引用和弱全局引用
jmethodID/jfielID和jobject没有继承关系,他不是个object,只是个整数,不存在被释放与否的问题,可用全局变量保存。
jclass是由jobject继承而来的类,所以它是个jobject,需要用弱全局引用来缓存jclass对象。
局部引用管理new出来的对象,注意及时delete。
总体原则,注意释放所有对jobject的引用。
缓存与不缓存的效果对比:调用速度提高40倍
设备 | 缓存 | 不缓存 |
NOTE3 | 12.06 | 420.82 |
NOTE4 | 7.96 | 495 |
NOTE5 | 4.3 | 277.42 |
NEXUS6P | 7.28 | 83.76 |
平均 | 7.9 | 319.25 |
倍数 | 40.4 |
TIPS:
1. 不同线程使用JNIEnv*对象,需要attachCurrentThread将env挂到当前线程,否则无法使用env。
2. 尽量避免频繁调用JNI或者是使用JNI传输大量到数据。
相关阅读
摘要:本章首先以应用程序开发者的角度审视Linux的进程内存管理,在此基础上逐步深入到内核中讨论系统物理内存管理和内核内存的使用