JNI 技术入门

什么是JNI?

Java Native Interface 即Java本地接口,它允许Java 代码和其他语言写的代码进行交互。提供了一些API 来与其他语言进行通信(主要是C/C++)。

为什么要使用JNI ?

在C/C++ 中写的程序,可以避开JVM 的内存开销过大的限制,处理高性能的计算,直接调用系统服务(如驱动)等功能。

就是这种情况下,用Java 代码没有C/C++效率高,使用JNI

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 方法的调用同样方法的耗时统计图,可以看到两者的耗时差距为11倍

原因:

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倍

TIPS:

1. 不同线程使用JNIEnv*对象,需要AttachCurrentThread将env挂到当前线程,否则无法使用env。

2. 尽量避免频繁调用JNI或者是使用JNI传输大量到数据。

转自:JNI内存管理

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值