java层与jni层的数据拷贝实现

在Java代码与Jni层之间经常会传递数组(byte[]),一个典型的应用是Java层把需要发送给客户端的数据流传递到Jni层,由Jni层的Socket代码发送出去,当然,Jni层也需要把从Socket接收到的数据流返回给Java层。我简单地总结了一下,从Java层到Jni层,从Jni层到JAVA层,各有3种传递方式,下面用代码示例简单地介绍一下。


示例代码的主要文件有两个,一个是Native.java,是Java层的类;另一个是Native.c,是JNI层的文件,关键的地方我都用注释添加到代码中了,完整的代码见博文后面的附件。


一、 从Java传递数组到Jni层


Jni层接收到Java层传递过来的byte[]数组,一般有2个函数来获取它的值,一个 GetByteArrayRegion,另一个是 GetByteArrayElements ,前者是进行值拷贝,将Java端数组的数据拷贝到本地的数组中,后者是指针的形式,将本地的数组指针直接指向Java端的数组地址,其实本质上是JVM在堆上分配的这个数组对象上增加一个引用计数,保证垃圾回收的时候不要释放,从而交给本地的指针使用,使用完毕后指针一定要记得通过ReleaseByteArrayElements进行释放,否则会产生内存泄露。


首先看Native.java的定义:


wKiom1MDPn-zrFquAAEfblr-1gM922.jpg


再看看对应的native.c的实现代码:


wKiom1MDTe_TtiK4AATD502gXFA360.jpg


二、 从Jni层传递数组到Java层


把Jni层定义的数组传递到Java层,一般有两种方法,一种是通过native函数的返回值来传递,另一种是通过jni层回调java层的函数来传递,后者多用于jni的线程中。无论哪种方法,都离不开 SetByteArrayRegion 函数,该函数将本地的数组数据拷贝到了 Java 端的数组中。下面只介绍前一种方式,即通过native函数返回值的方式传递jni层的数组,回调的方式其实用法类似,就不详细介绍了。


首先看Native.java的定义:


wKioL1MDR67SG0UaAAAuEYEh0M0437.jpg

   再看看native.c是如何实现的:


wKioL1MDR_WAzA_rAAG9mXZ_B5k926.jpg

由上述代码示例可以看出,首先通过 NewByteArray 在堆上分配数组对象,然后通过SetByteArrayRegion 把本地的数组数据拷贝到堆上分配的数组中去,然后通过返回值将分配的数组对象返回到Java层即可。对于回调的方式,这几步操作也是一样的,唯一的不同是,回调方式不是以返回值的方式将数组对象返回给Java层,而是在回调函数中,以回调函数参数的形式返回给Java层。


三、 Direct Buffer 方式传递


Java和Jni层的数组传递还有一个比较重要的方式,就是通过Direct Buffer来传递,这种方式类似于在堆上创建创建了一个Java和Jni层共享的整块内存区域,无论是Java层或者Jni层均可访问这块内存,并且Java端与Jni端同步变化,由于是采用的是共享内存的方式,因此相比于普通的数组传递,效率更高,但是由于构造/析构/维护这块共享内存的代价比较大,所以小数据量的数组建议还是采用上述方式,Direct Buffer方式更适合长期使用频繁访问的大块内存的共享。具体使用方法介绍如下:


首先看Native.java的定义:


wKioL1MDSijRi4H0AADi4k23LhI510.jpg

再看看native.c是如何实现的:


wKioL1No1RCyGE4CAAKxpODZMGQ205.jpg

由上述代码可以看出,其中使用起来还是很简单的,Jni层只需要通过GetDirectBufferAddress函数即可获取到这块共享的内存的地址,Direct Buffer的管理工作均由操作系统来负责。

转载自:Java层与Jni层的数组传递

==================================

Get<PrimitiveType>ArrayRegion Routines

void Get<PrimitiveType>ArrayRegion(JNIEnv *env, ArrayType array,
jsize start, jsize len,
 NativeType *buf);

A family of functions that copies a region of a primitive array into a buffer.

The following table describes the specific primitive array element accessors. You should do the following substitutions:

  • Replace Get<PrimitiveType>ArrayRegion with one of the actual primitive element accessor routine names from the following table.
  • Replace ArrayType with the corresponding array type.
  • Replace NativeType with the corresponding native type for that routine.
Get<PrimitiveType>ArrayRegion Family of Array Accessor Routines
Get<PrimitiveType>ArrayRegion Routine Array Type Native Type
GetBooleanArrayRegion() jbooleanArray jboolean
GetByteArrayRegion() jbyteArray jbyte
GetCharArrayRegion() jcharArray jchar
GetShortArrayRegion() jshortArray jhort
GetIntArrayRegion() jintArray jint
GetLongArrayRegion() jlongArray jlong
GetFloatArrayRegion() jfloatArray jloat
GetDoubleArrayRegion() jdoubleArray jdouble

LINKAGE:

Indices in the JNIEnv interface function table.

PARAMETERS:

env: the JNI interface pointer.

array: a Java array.

start: the starting index.

len: the number of elements to be copied.

buf: the destination buffer.

THROWS:

ArrayIndexOutOfBoundsException: if one of the indexes in the region is not valid.


Set<PrimitiveType>ArrayRegion Routines

void Set<PrimitiveType>ArrayRegion(JNIEnv *env, ArrayType array,
jsize start, jsize len,
 const NativeType *buf);

A family of functions that copies back a region of a primitive array from a buffer.

The following table describes the specific primitive array element accessors. You should make the following replacements:

  • Replace Set<PrimitiveType>ArrayRegion with one of the actual primitive element accessor routine names from the following table.
  • Replace ArrayType with the corresponding array type.
  • Replace NativeType with the corresponding native type for that routine.
Set<PrimitiveType>ArrayRegion Family of Array Accessor Routines
Set<PrimitiveType>ArrayRegion Routine Array Type Native Type
SetBooleanArrayRegion() jbooleanArray jboolean
SetByteArrayRegion() jbyteArray jbyte
SetCharArrayRegion() jcharArray jchar
SetShortArrayRegion() jshortArray jhort
SetIntArrayRegion() jintArray jint
SetLongArrayRegion() jlongArray jlong
SetFloatArrayRegion() jfloatArray jloat
SetDoubleArrayRegion() jdoubleArray jdouble

LINKAGE:

Indices in the JNIEnv interface function table.

PARAMETERS:

env: the JNI interface pointer.

array: a Java array.

start: the starting index.

len: the number of elements to be copied.

buf: the source buffer.

THROWS:

ArrayIndexOutOfBoundsException: if one of the indexes in the region is not valid.


ewDirectByteBuffer

NewDirectByteBuffer

jobject NewDirectByteBuffer(JNIEnv* env, void* address, jlong capacity);

Allocates and returns a direct java.nio.ByteBuffer referring to the block of memory starting at the memory address address and extending capacity bytes.

Native code that calls this function and returns the resulting byte-buffer object to Java-level code should ensure that the buffer refers to a valid region of memory that is accessible for reading and, if appropriate, writing. An attempt to access an invalid memory location from Java code will either return an arbitrary value, have no visible effect, or cause an unspecified exception to be thrown.

LINKAGE:

Index 229 in the JNIEnv interface function table.

PARAMETERS:

env: the JNIEnv interface pointer

address: the starting address of the memory region (must not be NULL)

capacity: the size in bytes of the memory region (must be positive)

RETURNS:

Returns a local reference to the newly-instantiated java.nio.ByteBuffer object. Returns NULL if an exception occurs, or if JNI access to direct buffers is not supported by this virtual machine.

EXCEPTIONS:

OutOfMemoryError: if allocation of the ByteBuffer object fails

SINCE:

JDK/JRE 1.4


转载自: oracle官网 the jni array operations - JNI Functions 

猜你喜欢

转载自blog.csdn.net/qq_26222859/article/details/80900125
今日推荐