Java中JNI的使用(下)
Posted ssming
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java中JNI的使用(下)相关的知识,希望对你有一定的参考价值。
数组的操作
数组是一个很常用的数据类型,在但是在 JNI 中并不能直接操作 jni 数组(比如 jshortArray、jfloatArray)。使用方法是:
- 获取数组长度:
jsize GetArrayLength(jarray array)
- 创建新数组:
ArrayType New<PrimitiveType>Array(jsize length);
- 通过JNI数组获取一个C/C++数组:
<type>* Get<type>ArrayElements(jshortArray array, jboolean *isCopy)
- 指定原数组的范围获取一个C/C++数组(该方法只针对于原始数据数组,不包括Object数组):
void Get<PrimitiveType>ArrayRegion(JNIEnv *env, ArrayType array, jsize start, jsize len, NativeType *buf);
- 设置数组元素:
void Set<type>ArrayRegion(jshortArray array, jsize start, jsize len,const <type> *buf)
。again,如果是Object数组需要使用:void SetObjectArrayElement(JNIEnv *env, jobjectArray array, jsize index, jobject value);
- 使用完之后,释放数组:
void Release<type>ArrayElements(jshortArray array, jshort *elems, jint mode)
有点要说明的:
1、上面的3中的 isCopy:当你调用 getArrayElements 时 JVM(Runtime)可以直接返回数组的原始指针,或者是 copy 一份,返回给你,这是由 JVM 决定的。所以 isCopy 就是用来记录这个的。他的值是 JNI_TURE
或者 JNI_FALSE
。
2、6释放数组。一定要释放你所获得数组。其中有一个mode
参数,其有三个可选值,分别表示:
- 0
- 原始数组:允许原数组被垃圾回收。
- copy: 数据会从get返回的buffer copy回去,同时buffer也会被释放。
- JNI_COMMIT
- 原始数组:什么也不做
- copy: 数据会从get返回的buffer copy回去,同时buffer不会被释放。
- JNI_ABORT
- 原始数组:允许原数组被垃圾回收。之前由JNI_COMMIT提交的对数组的修改将得以保留。
- copy: buffer会被释放,同时buffer中的修改将不会copy回数组!
关于引用与垃圾回收
比如上面有个方法传了一个 jobject 进来,然后我把她保存下来,方便以后使用。这样做是不行哒!因为他是一个 LocalReference,所以不能保证 jobject 指向的真正的实例不被回收。也就是说有可能你用的时候那个指针已经是个野指针的。然后你的程序就直接 Segment Fault 了,呵呵。
在JNI中提供了三种类型的引用:
- Local Reference:即本地引用。在JNI层的函数,所有非全局引用对象都是Local Reference, 它包括函数调用是传入的jobject和JNI成函数创建的jobject。Local Reference的特点是一旦JNI层的函数返回,这些jobject就可能被垃圾回收。
- Glocal Reference:全局引用,这些对象不会主动释放,永远不会被垃圾回收。
- Weak Glocal Reference:弱全局引用,一种特殊的Global Reference,在运行过程中有可能被垃圾回收。所以使用之前需要使用
jboolean IsSameObject(jobject obj1, jobject obj2)
判断它是否已被回收。
Glocal Reference:
1. 创建:jobject NewGlobalRef(jobject lobj);
2. 释放:void DeleteGlobalRef(jobject gref);
Local Reference:
LocalReference也有一个释放的函数:void DeleteLocalRef(jobject obj)
,他会立即释放Local Reference。 这个方法可能略显多余,其实也是有它的用处的。刚才说Local Reference会再函数返回后释放掉,但是假如函数返回前就有很多引用占了很多内存,最好函数内就尽早释放不必要的内存。
关于JNI_OnLoad
开头提到 JNI_OnLoad 是 Java1.2 中新增加的方法,对应的还有一个 JNI_OnUnload,分别是动态库被 JVM 加载、卸载的时候调用的函数。有点类似于 Windows 里的 DllMain。
前面提到的实现对应 native 的方法是实现 javah 生成的头文件中定义的方法,这样有几个弊端:
- 函数名太长。很长,相当长。
- 函数会被导出,也就谁说可以在动态库的导出函数表里面找到这些函数。这将有利于别人对动态库的逆向工程,因此带来安全问题。
现在有了JNI_OnLoad,情况好多了。你不光能在其中完成动态注册 native 函数的工作还可以完成一些初始化工作。Java 对应的有了 jint RegisterNatives(jclass clazz, const JNINativeMethod *methods,jint nMethods)
函数。参数分别是:
- jclass clazz,于native层对应的java class
- const JNINativeMethod *methods这是一个数组,数组的元素是JNI定义的一个结构体JNINativeMethod
- 上面的数组的长度
JNINativeMethod:代码中的定义如下
1
2
3
4
5
6
7
8
9
10
|
/* * used in RegisterNatives to describe native method name, signature, * and function pointer. */ typedef struct { char *name; char *signature; void *fnPtr; } JNINativeMethod; |
所以他有三个字段,分别是
于是现在你可以不用导出 native 函数了,而且可以随意给函数命名,唯一要保证的是参数及返回值的统一。然后需要一个 const JNINativeMethod *methods
数组来完成映射工作。
以上是关于Java中JNI的使用(下)的主要内容,如果未能解决你的问题,请参考以下文章