在Java中當我們要對數據進行更底層的操作時,一般是操作數據的字節(byte)形式,這時經常會用到ByteBuffer這樣一個類。
ByteBuffer提供了兩種靜態實例方式:
- public static ByteBuffer allocate(int capacity)
- 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來說更快。
可以根據下面的測試代碼測試:
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是創建效率低,讀取和寫入的效率高。