LocalRef与GlobalRef
前者类似局部变量(类似,但是又有本质区别),后者类似全局变量前者在 一次jni调用结束后,由jvm自动释放
后者需要 手动释放
LocalRef内存泄露
LocalRef与局部变量不同,它是保存在一张表中的,这张表在调用jni开始创建,一次jni调用结束后销毁注意: LocalRef的生命周期不是一个函数,而是一次jni调用,这一点与局部变量不同
另外,如果表满,那么会出现OOM
所以,比较好的做法是,如果不用,就调用DeleteLocalRef来将这个ref从表中删除,不要等着自动释放
ReleaseStringChars
声明这个字符串指针不再使用,可以当做是delete来看处理的是unicode类型
加速技巧
- 缓存常用的ref(视情况,如果涉及到包名之类的,为兼容考虑不缓存)
- 数组操作时,使用相应的有region的方法,对需要处理的部分数组做处理,而不是全部,因为每次处理都会有对应数量的副本产生,会耗时
- 一次处理数组操作,而不是分多次多数组的多个区域做操作,因为每次操作都会耗时
- 尽可能用参数而不是封装的对象做参数,因为获取封装对象的field需要耗时,而直接给参数会快很多
- 尽可能少的java和native之间的互调,可以简单认为:java层尽可能少的调用native函数,争取一次做完动作;相应的,native层尽可能少的调用jni相关的函数
- 设计良好的数据结构,使之符合上一条的技巧
- 在不需要时,调用DeleteLocalRef来删除本地引用
- EnsureLocalCapacity() 方法来通知 JVM 您将使用超过 16 个本地引用,不到万不得已,请勿尝试
防错技巧
- jni的env的跨线程处理,如果要跨线程使用,记得先缓存起来
- 检查异常,不是检查是否为null,而是直接对异常做处理 if((*env)->ExceptionOccurred(env)) { return; }
- 检查返回值,即:检查是否为null
- 在使用GetXXX获取数组时,不同的JVM处理是不同的,有的返回副本,有的直接返回,如果返回副本的,需要调用ReleaseXXX,并使用mode:JNI_COMMIT(0)来复制副本到实际数据并释放,否则,实际数据可能不会有任何改变,因为你一直在对副本做操作
- 确保代码不会在 GetXXXCritical() 和 ReleaseXXXCritical() 调用之间发起任何 JNI 调用或由于任何原因出现阻塞------原因暂时不明
- 记得删除GlobleRef,否则会有内存泄露