1. 程式人生 > >Java直接記憶體和堆記憶體的效能比較

Java直接記憶體和堆記憶體的效能比較


在JDK 1.4中新加入了NIO(New Input/Output)類,引入了一種基於通道(Channel)與緩衝區(Buffer)的I/O方式,

它可以使用Native函式庫直接分配堆外記憶體,然後通過一個儲存在Java堆裡面的DirectByteBuffer物件作為這塊記憶體的引用進行操作。

這樣能在一些場景中顯著提高效能,因為避免了在Java堆和Native堆中來回複製資料。 

顯然,本機直接記憶體的分配不會受到Java堆大小的限制,

但是,既然是記憶體,則肯定還是會受到本機總記憶體(包括RAM及SWAP區或者分頁檔案)的大小及處理器定址空間的限制。

伺服器管理員配置虛擬機器引數時,一般會根據實際記憶體設定-Xmx等引數資訊,但經常會忽略掉直接記憶體,使得各個記憶體區域的總和大於實體記憶體限制(包括物理上的和

作業系統級的限制),從而導致動態擴充套件時出現OutOfMemoryError異常。

上面這段話引用自該文章

程式碼實驗

注意到上面這段話中的黑體字:這樣能在一些場景中顯著提高效能,因為避免了在Java堆和Native堆中來回複製資料,然而這個“一些場景”具體指什麼呢?我們通過程式碼做個實驗看看。

  1. package com.winwill.jvm;  
  2. import org.junit.Test;  
  3. import java.nio.ByteBuffer;  
  4. /** 
  5.  * @author qifuguang 
  6.  * @date 15-5-26 下午8:23 
  7.  */
  8. publicclass TestDirectMemory {  
  9.     /** 
  10.      * 測試DirectMemory和Heap讀寫速度。 
  11.      */
  12.     @Test
  13.     publicvoid testDirectMemoryWriteAndReadSpeed() {  
  14.         long tsStart = System.currentTimeMillis();  
  15.         ByteBuffer buffer = ByteBuffer.allocateDirect(400);  
  16.         for (int i = 0; i < 100000; i++) {  
  17.             for (int j = 0; j < 100; j++) {  
  18.                 buffer.putInt(j);  
  19.             }  
  20.             buffer.flip();  
  21.             for (byte j = 0; j < 100; j++) {  
  22.                 buffer.getInt();  
  23.             }  
  24.             buffer.clear();  
  25.         }  
  26.         System.out.println("DirectMemory讀寫耗用: " + (System.currentTimeMillis() - tsStart) + " ms");  
  27.         tsStart = System.currentTimeMillis();  
  28.         buffer = ByteBuffer.allocate(400);  
  29.         for (int i = 0; i < 100000; i++) {  
  30.             for (int j = 0; j < 100; j++) {  
  31.                 buffer.putInt(j);  
  32.             }  
  33.             buffer.flip();  
  34.             for (byte j = 0; j < 100; j++) {  
  35.                 buffer.getInt();  
  36.             }  
  37.             buffer.clear();  
  38.         }  
  39.         System.out.println("Heap讀寫耗用: " + (System.currentTimeMillis() - tsStart) + " ms");  
  40.     }  
  41.     /** 
  42.      * 測試DirectMemory和Heap記憶體申請速度。 
  43.      */
  44.     @Test
  45.     publicvoid testDirectMemoryAllocate() {  
  46.         long tsStart = System.currentTimeMillis();  
  47.         for (int i = 0; i < 100000; i++) {  
  48.             ByteBuffer buffer = ByteBuffer.allocateDirect(400);  
  49.         }  
  50.         System.out.println("DirectMemory申請記憶體耗用: " + (System.currentTimeMillis() - tsStart) + " ms");  
  51.         tsStart = System.currentTimeMillis();  
  52.         for (int i = 0; i < 100000; i++) {  
  53.             ByteBuffer buffer = ByteBuffer.allocate(400);  
  54.         }  
  55.         System.out.println("Heap申請記憶體耗用: " + (System.currentTimeMillis() - tsStart) + " ms");  
  56.     }  
  57. }  

執行這段程式碼,得到的結果如下: 


結論

從上面的實驗結果可以看出,直接記憶體在讀和寫的效能都優於堆內記憶體,但是記憶體申請速度卻不如堆內記憶體,所以可以歸納一下: 

直接記憶體適用於不常申請,但是需要頻繁讀取的場景,

在需要頻繁申請的場景下不應該使用直接記憶體(DirectMemory),而應該使用堆內記憶體(HeapMemory)。