转载
这部分算是 JNI 的基本内容, 理所当然的有一大坨接口来干这些事情,
比如 NewString, GetStringChars, GetArrayLength, NewByteArray
需要注意的只是, 有的类型不需要释放, 有的类型则需要, 例如对象可能需要 DeleteLocalRef 或 DeleteGlobalRef,
访问数组内容一般需要 ReleaseByteArrayElements
// Java
public Object func() {...}
// JNI 这种都是临时对象 todo!!!!!!
jobject obj = env->CallObjectMethod(...);
这种情况一般不同担心, JNI 端代码结束后, 一般会在合适时机 GC
当然, 特殊情况是, 假如 JNI 端需要持续执行较长时间, 并且可能访问了较多的 Java 端对象,
就需要手动调用 DeleteLocalRef 释放这些临时对象, 避免性能问题, 此外, JNI 端 local 对象个数也是有限制的
同理的还有 NewString 等 JNI 接口, 创建的都是临时对象
上面提到了, 临时对象会在不确定时机被 GC, 所以如果你要长期使用这个对象, 简单的保存 jobject 是不行的
正确的做法是, 使用 NewGlobalRef 来创建一个全局引用, 这个引用会一直存在, 不会被 GC, 直到你调用 DeleteGlobalRef
这边需要注意的是, NewGlobalRef 虽然是操作同一个对象, 但是 jobject 本身是不一样的, 典型的使用方法是:
jobject localRef = xxx;
jobject globalRef = env->NewGlobalRef(localRef);
env->DeleteLocalRef(localRef);
这是比较常见也比较容易实现的方式, 典型使用场景是, 内存块的申请/使用/释放 (也可以是 C++ 对象), 都在 JNI 端处理, Java 端只负责调用和这个指针的传递
PS:
有点担心哪天升级 128 位 CPU 了咋办, 不过目前大多 JNI 都是这种处理方式, 应该未来会有变通方式解决吧
更保险起见的方式是将指针转换为 byte[] 进行传递, 不过麻烦而且性能会受影响
内存块的传递
典型使用场景:
Java 读取图片或音频什么的, 把内容作为内存块交给 JNI 处理, JNI 处理完后, 把新的内存块内容返回给 Java 端再重新解析为图片或音频什么的
(这里姑且不论使用临时文件做中转的方式, 何况写文件也挺慢的呢)
这个可谓麻烦至极, 首先, 通常都会想到用 byte[] 传递,
然而这货无论是 Java 传到 JNI 还是 JNI 传到 Java, 都 (可能但不一定) 需要进行深拷贝,
对于分分钟上兆的媒体文件来说, 是个巨大的 CPU 和内存开销
GetPrimitiveArrayCritical
看上去好像那么一回事, 写个简单的测试代码发现可以用, 皆大欢喜了?
顾名思义, 这货是直接访问 Java 的底层数据内容, 对于 Java 这种不知何时会 GC 的运行时来说,
Java 显然不会让你瞎搞, 试想以下流程:
GetPrimitiveArrayCritical 得到底层数据指针
JNI 端调用 Java 代码, 很可能产生 GC
java.nio.ByteBuffer
有了 ByteBuffer, JNI 端就可以通过 GetDirectBufferAddress 获得内存地址, 完美了… 吗?
图样图森破, 这货依然有着麻烦的使用条件:
虽然都叫 ByteBuffer, 但是这个只能使用 NewDirectByteBuffer 或者 ByteBuffer.allocateDirect() 进行创建,
否则 GetDirectBufferAddress 返回的总是 NULL
不支持的几个: ByteBuffer.allocate(), ByteBuffer.wrap()
更具体的原因, 各位有兴趣可以去搜搜 DirectByteBuffer 和 HeapByteBuffer
JNI 这魂淡没有提供足够的方法去操作 ByteBuffer 的方法
比如 position(), remaining(), flip() 都没有, 而只有 GetDirectBufferCapacity 来获取最大容量,
所以你还得自行添加一大坨内容, 来在 JNI 调用这些方法
over
转载请注明来自: http://zsaber.com/blog/p/107
既然都来了, 有啥想法顺便留个言呗? (无奈小广告太多, 需审核, 见谅)