NIO的Buffer提供了一個可以不經過JVM內存直接訪問系統物理內存的類——DirectBuffer。 DirectBuffer類繼承自ByteBuffer,但和普通的ByteBuffer不同,普通的ByteBuffer仍在JVM堆上分配內存,其最大內存受到最大堆內存的限制;而DirectBuffer直接分配在物理內存中,並不占用堆空間,其可申請的最大內存受操作系統限制。
直接內存的讀寫操作比普通Buffer快,但它的創建、銷毀比普通Buffer慢(猜測原因是DirectBuffer需向OS申請內存涉及到用戶態內核態切換,而后者則直接從堆內存划內存即可)。
因此直接內存使用於需要大內存空間且頻繁訪問的場合,不適用於頻繁申請釋放內存的場合。
(Note:DirectBuffer並沒有真正向OS申請分配內存,其最終還是通過調用Unsafe的allocateMemory()來進行內存分配。不過JVM對Direct Memory可申請的大小也有限制,可用-XX:MaxDirectMemorySize=1M設置,這部分內存不受JVM垃圾回收管理。)
使用對外內存的原因:
- 對垃圾回收停頓的改善。由於堆外內存是直接受操作系統管理而不是JVM,所以當我們使用堆外內存時,即可保持較小的堆內內存規模。從而在GC時減少回收停頓對於應用的影響。
- 提升程序I/O操作的性能。通常在I/O通信過程中,會存在堆內內存到堆外內存的數據拷貝操作,對於需要頻繁進行內存間數據拷貝且生命周期較短的暫存數據,都建議存儲到堆外內存。
以下是一些測試:
代碼:

1 class DirectMemory { 2 3 // 分配堆內存 4 public static void bufferAccess() { 5 long startTime = System.currentTimeMillis(); 6 ByteBuffer b = ByteBuffer.allocate(500); 7 for (int i = 0; i < 1000000; i++) { 8 for (int j = 0; j < 99; j++) 9 b.putInt(j); 10 b.flip(); 11 for (int j = 0; j < 99; j++) 12 b.getInt(); 13 b.clear(); 14 } 15 long endTime = System.currentTimeMillis(); 16 System.out.println("access_nondirect:" + (endTime - startTime)); 17 } 18 19 // 直接分配內存 20 public static void directAccess() { 21 long startTime = System.currentTimeMillis(); 22 ByteBuffer b = ByteBuffer.allocateDirect(500); 23 for (int i = 0; i < 1000000; i++) { 24 for (int j = 0; j < 99; j++) 25 b.putInt(j); 26 b.flip(); 27 for (int j = 0; j < 99; j++) 28 b.getInt(); 29 b.clear(); 30 } 31 long endTime = System.currentTimeMillis(); 32 System.out.println("access_direct:" + (endTime - startTime)); 33 } 34 35 public static void bufferAllocate() { 36 long startTime = System.currentTimeMillis(); 37 for (int i = 0; i < 1000000; i++) { 38 ByteBuffer.allocate(1000); 39 } 40 long endTime = System.currentTimeMillis(); 41 System.out.println("allocate_nondirect:" + (endTime - startTime)); 42 } 43 44 public static void directAllocate() { 45 long startTime = System.currentTimeMillis(); 46 for (int i = 0; i < 1000000; i++) { 47 ByteBuffer.allocateDirect(1000); 48 } 49 long endTime = System.currentTimeMillis(); 50 System.out.println("allocate_direct:" + (endTime - startTime)); 51 } 52 53 public static void main(String args[]) { 54 System.out.println("訪問性能測試:"); 55 bufferAccess(); 56 directAccess(); 57 58 System.out.println(); 59 60 System.out.println("分配性能測試:"); 61 bufferAllocate(); 62 directAllocate(); 63 } 64 }
結果:
訪問性能測試: access_nondirect:157 access_direct:134 分配性能測試: allocate_nondirect:231 allocate_direct:613
可見與在JVM堆分配內存(allocate)相比,直接內存分配(allocateDirect)的訪問性能更好,但分配較慢。(一般如此,當然數據量小的話差別不是那么明顯)