0
点赞
收藏
分享

微信扫一扫

NDK开发与实践(入门篇·微课视频版)

小云晓云 1天前 阅读 1

1.1      DirectBuffer的使用场景

当使用byte[]数组时,Java代码需要将数据从Java堆复制到原生内存(例如,通过JNI的SetByteArrayRegion()或GetByteArrayRegion()函数),然后原生代码才能访问这些数据。这种数据复制操作可能会带来额外的性能开销。在某些实现中,可以使用GetByteArrayElements()和 GetPrimitiveArrayCritical()函数获取指向托管堆中原始数据的实际指针,但在其他实现中,它会在原生堆上分配缓冲区并复制数据,所以,byte[]传递是否能获得真正的原始数据的指针,取决于虚拟机的实现,而直接字节缓冲区允许原生代码直接访问其内存区域,无须进行这种复制操作。

1.1.1  大数据量的IO密集型操作

对于需要处理大量数据的IO密集操作,如文件读写、网络通信等,DirectBuffer可以

显著提高性能。由于DirectBuffer的内存分配在JVM堆外,因此可以避免在Java堆内存和操作系统之间复制数据的需要,从而减少了数据处理的时间和CPU的负载。

1.1.2  长期使用的数据

对于那些需要长期使用的数据,使用DirectBuffer可以避免频繁地创建和销毁堆内

Buffer所带来的额外开销。由于DirectBuffer的生命周期内的内存地址都不会再发生更改,因此内核可以安全地对其进行访问。

1.1.3  对内存管理有特殊要求的场景

DirectBuffer的使用降低了垃圾收集的压力,因为它们不受JVM垃圾收集的直接管

理。这在一些对内存管理有特殊要求的场景中可能非常有用,例如需要避免频繁垃圾收集导致的性能波动或延迟。


1.1.4  需要直接访问操作系统内存资源的场景

DirectBuffer提供了一种高效的方式来直接访问和操作系统级别的内存资源。这种方式允许Java应用程序能够更接近操作系统的底层,提供了更为高效的数据处理能力。


1.2      DirectBuffer的使用案例

示例代码将演示在原生代码中申请缓冲区,并通过JNI接口使其可被Java代码访问。在Java端,利用获得到的缓冲区的引用将数据高效地传递到原生代码中。同时,示例代码将记录两种传递方式分别运行10000000次的耗时。


代码如下:

//第9章/MainActivity.java

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";

    static {

        System.loadLibrary("directbuffer");

    }


    ByteBuffer mByteBuffer;

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);


        //获得native中申请的缓冲区

        mByteBuffer = getNativeByteBuffer();

        //申请一个byte数组

        byte[] bytes = new byte[10240];

        //初始化数组内容

        for (int i = 0; i < 10240; i++) {

            bytes[i] = (byte) i;

        }

        //将byte数组放到bytebuffer中

        mByteBuffer.put(bytes);

        int count = 10000000;

        //打印时间

        Log.e(TAG, "onCreate: sendData start time: "  + SystemClock.currentThreadTimeMillis());

        while (count > 0){

          //通知原生代码操作数据

            sendData();

            count --;

        }

        Log.e(TAG, "onCreate: sendData end time: "  + SystemClock.currentThreadTimeMillis());


        count = 1000000;



        //打印时间

        Log.e(TAG, "onCreate: setByteData start time: "  + SystemClock.currentThreadTimeMillis());

        while (count > 0){

          //将byte[]传递到原生代码中

            setByteData(bytes);

            count --;

        }

        Log.e(TAG, "onCreate: setByteData end time: "  + SystemClock.currentThreadTimeMillis());

    }


    /**

     * 获取native申请的DirectBuffer

     * @return  DirectBuffer

     */

    public native ByteBuffer getNativeByteBuffer();


    /**

     * 通知原生代码操作数据

     */

    public native void sendData();


    /**

     * 使用byte[]数组传递方式

     * @param data byte[]

     */

    public native void setByteData(byte[] data);

}


原生代码实现代码如下:

//第9章/native-lib.c

#include <jni.h>

#include <string.h>

#include <malloc.h>



#include <android/log.h> //添加头文件

#define LOG_TAG "jni" //定义TAG

#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)



unsigned  char *gl_buffer = NULL; //DirectBuffer地址

unsigned  int gl_capacity;        //DirectBuffer 容量


unsigned  char buffer[10240] = {0};  //用来模拟数据的复制



JNIEXPORT jobject JNICALL

Java_com_example_directbuffer_MainActivity_getNativeByteBuffer(JNIEnv *env, jobject thiz) {

    if (NULL == gl_buffer){

        gl_buffer = malloc(10240);

    }


    return (*env)->NewDirectByteBuffer(env, gl_buffer, 10240);

}


JNIEXPORT void JNICALL

Java_com_example_directbuffer_MainActivity_sendData(JNIEnv *env, jobject thiz) {

    //由于是DirectBuffer,这里可以直接使用gl_buffer,无须复制

}


JNIEXPORT void JNICALL

Java_com_example_directbuffer_MainActivity_setByteData(JNIEnv *env, jobject thiz, jbyteArray data) {

    //模拟数据的使用,将数组内容复制到gl_buffer中才能使用

    (*env)->GetByteArrayRegion(env, data, 0, 10240, gl_buffer);

}


       运行结果如下:

//使用DirectBuffer,10000000次执行耗时66ms

sendData start time: 117

sendData end time: 183


//直接传递byte[],10000000次执行耗时2021ms

setByteData start time: 183

setByteData end time: 2204


对于不同次数比较的运行结果,见表9-1。


表 9‑1 运行结果比较

运行次数

DirectBuffer

byte[]

差值(ms

1000

0

0

0

10000

3

4

1

100000

12

24

12

1000000

18

208

190

10000000

66

2021

1955


从运行结果可以看出,随着运行次数的增加,相较于使用byte[],使用DirectBuffer对性能的提升也就越明显。

举报

相关推荐

0 条评论