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是創建效率低,讀取和寫入的效率高。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM