• NIO ByteBuffer的allocate与allocateDirect区别(HeapByteBuffer与DirectByteBuffer的区别)


    在Java中当我们要对数据进行更底层的操作时,一般是操作数据的字节(byte)形式,这时经常会用到ByteBuffer这样一个类。

    ByteBuffer提供了两种静态实例方式: 

    1. public static ByteBuffer allocate(int capacity)  
    2. public static ByteBuffer allocateDirect(int capacity)  


    为什么要提供两种方式呢?这与Java的内存使用机制有关。

    第一种分配方式产生的内存开销是在JVM中的,而另外一种的分配方式产生的开销在JVM之外,也就是系统级的内存分配。

    当Java程序接收到外部传来的数据时,首先是被系统内存所获取,然后在由系统内存复制复制到JVM内存中供Java程序使用。

    所以在另外一种分配方式中,能够省去复制这一步操作,效率上会有所提高。可是系统级内存的分配比起JVM内存的分配要耗时得多,所以并非不论什么时候allocateDirect的操作效率都是最高的。

    以下是一个不同容量情况下两种分配方式的操作时间对照: 

    由图能够看出,当操作数据量非常小时,两种分配方式操作使用时间基本是同样的,第一种方式有时可能会更快,可是当数据量非常大时,另外一种方式会远远大于第一种的分配方式。

    其中allocateDirect分配的字节缓冲区用中文叫做直接缓冲区(DirectByteBuffer),用allocate分配的ByteBuffer叫做堆字节缓冲区(HeapByteBuffer)..

    其实根据类名就可以看出,HeapByteBuffer所创建的字节缓冲区就是在jvm堆中的,即内部所维护的java字节数组。

    而DirectByteBuffer是直接操作操作系统本地代码创建的内存缓冲数组(c、c++的数组)。

    由于DirectByteBuffer操作的缓冲区是通过操作系统本地代码创建的,对于java来说创建和销毁DirectByteBuffer更消耗性能。

    而HeapByteBuffer内部是直接创建的java数组,对于java来说更快。

    可以根据下面的测试代码测试:

    github源码地址

    package NIO_FileChannel;
    
    import java.nio.ByteBuffer;
    
    public class DirectAndHeapSpeedCompare {
    
        public static void main(String[] args) {
            directAndHeapSpeedCompare();
        }
    
        private static void directAndHeapSpeedCompare() {
            int length = 10000;
            heapExecuteTime(length);
            directExecuteTime(length);
        }
    
        private static void directExecuteTime(int length) {
            long startTime = System.currentTimeMillis();
            ByteBuffer[] byteBufferArray = new ByteBuffer[length];
            for (int i = 0; i < length; i++) {
                byteBufferArray[i] = ByteBuffer.allocateDirect(1024);
            }
            long endTime = System.currentTimeMillis();
            System.out.println("创建" + length + "个DirectByteBuffer所消耗的时间:" + (endTime - startTime));
        }
    
        private static void heapExecuteTime(int length) {
            long startTime = System.currentTimeMillis();
            ByteBuffer[] byteBufferArray = new ByteBuffer[length];
            for (int i = 0; i < length; i++) {
                byteBufferArray[i] = ByteBuffer.allocate(1024);
            }
            long endTime = System.currentTimeMillis();
            System.out.println("创建" + length + "个HeapByteBuffer所消耗的时间:" + (endTime - startTime));
        }
    }
    

       

    创建10000个HeapByteBuffer所消耗的时间:12
    创建10000个DirectByteBuffer所消耗的时间:17
    

      

    那么为什么创建DirectByteBuffer比HeapByteBuffer性能差却还使用DirectByteBuffer呢?

    答:创建DirectByteBuffer的确不如HeapByteBuffer快,但是本地IO(从操作系统本地获取数据,比如文件、socket网络数据)修改数据DirectByteBuffer是更快的。

    因为:

    HeapByteBuffer的缓冲区是java中的java数组,使用HeapByteBuffer读取一个文件,操作系统会先把文件读取到操作系统管理的内存中。

    然后在把操作系统管理的这块内存的数据复制到jvm管理的内存(即HeapByteBuffer管理的java字节数组)中,然后HeapByteBuffer对java字节数组进行操作。。

    而DirectByteBuffer

    (1)先在操作系统内核所管理的内存缓冲区中(文件系统页)开辟一块内存空间(作为缓冲区),文件数据可以直接从操作系统复制到该缓冲区中

    (2)用户写入和读取数据就可以直接操作这块缓冲区。。。。比使用HeapByteBuffer少了一个步骤,效率自然提升了。

    什么情况下使用DirectByteBuffer(ByteBuffer.allocateDirect(int))?

    1、频繁的native IO,即缓冲区 中转 从操作系统获取的文件数据、或者使用缓冲区中转网络数据等

    2、不需要经常创建和销毁DirectByteBuffer对象

    3、经常复用DirectByteBuffer对象,即经常写入数据到DirectByteBuffer中,然后flip,再读取出来,最后clear。。反复使用该DirectByteBuffer对象。

    而且,DirectByteBuffer不会占用堆内存。。也就是不会受到堆大小限制,只在DirectByteBuffer对象被回收后才会释放该缓冲区。

    什么情况下使用HeapByteBuffer(ByteBuffer.allocate(int))?

    1、同一个HeapByteBuffer对象很少被复用,并且该对象经常是用一次就不用了,此时可以使用HeapByteBuffer,因为创建HeapByteBuffer开销比DirectByteBuffer低。

    (但是!!创建所消耗时间差距只是一倍以下的差距,一般一次只会创建一个DirectByteBuffer对象反复使用,而不会创建几百个DirectByteBuffer,

    所以在创建一个对象的情况下,HeapByteBuffer并没有什么优势,所以,开发中要使用ByteBuffer时,直接用DirectByteBuffer就行了)

    总结:

    HeapByteBuffer和DirectByteBuffer

    1、HeapByteBuffer就是创建效率高,读取和写入的效率低。

    2、DirectByteBuffer是创建效率低,读取和写入的效率高。

  • 相关阅读:
    Android高性能ORM数据库DBFlow入门
    PHP数据库操作
    Smarty模板技术学习(二)
    Smarty模板技术学习
    微信开发简单实例
    支持IE6只是其一!Web开发的十大噩梦
    前端构建工具发展及其比较
    React通过redux-persist持久化数据存储
    vue中Axios的封装和API接口的管理
    yarn使用教程
  • 原文地址:https://www.cnblogs.com/felixzh/p/12013017.html
Copyright © 2020-2023  润新知