概述
直接內存並不是虛擬機運行時數據區的一部分,也不是Java 虛擬機規範中農定義的內存區域。在JDK1.4 中新加入了NIO(New Input/Output)類,引入了一種基於通道(Channel)與緩衝區(Buffer)的I/O 方式,它可以使用native 函數庫直接分配堆外內存,然後通脫一個存儲在Java堆中的DirectByteBuffer 對象作爲這塊內存的引用進行操作。這樣能在一些場景中顯著提高性能,因爲避免了在Java堆和Native堆中來回複製數據。
本機直接內存的分配不會受到Java 堆大小的限制,受到本機總內存大小限制
配置虛擬機參數時,不要忽略直接內存 防止出現OutOfMemoryError異常
直接內存(堆外內存)與堆內存比較
- 直接內存申請空間耗費更高的性能,當頻繁申請到一定量時尤爲明顯
- 直接內存IO讀寫的性能要優於普通的堆內存,在多次讀寫操作的情況下差異明顯
代碼驗證:
package com.xnccs.cn.share;
import java.nio.ByteBuffer;
/**
* 直接內存 與 堆內存的比較
*/
public class ByteBufferCompare {
public static void main(String[] args) {
allocateCompare(); //分配比較
operateCompare(); //讀寫比較
}
/**
* 直接內存 和 堆內存的 分配空間比較
*
* 結論: 在數據量提升時,直接內存相比非直接內的申請,有很嚴重的性能問題
*
*/
public static void allocateCompare(){
int time = 10000000; //操作次數
long st = System.currentTimeMillis();
for (int i = 0; i < time; i++) {
//ByteBuffer.allocate(int capacity) 分配一個新的字節緩衝區。
ByteBuffer buffer = ByteBuffer.allocate(2); //非直接內存分配申請
}
long et = System.currentTimeMillis();
System.out.println("在進行"+time+"次分配操作時,堆內存 分配耗時:" + (et-st) +"ms" );
long st_heap = System.currentTimeMillis();
for (int i = 0; i < time; i++) {
//ByteBuffer.allocateDirect(int capacity) 分配新的直接字節緩衝區。
ByteBuffer buffer = ByteBuffer.allocateDirect(2); //直接內存分配申請
}
long et_direct = System.currentTimeMillis();
System.out.println("在進行"+time+"次分配操作時,直接內存 分配耗時:" + (et_direct-st_heap) +"ms" );
}
/**
* 直接內存 和 堆內存的 讀寫性能比較
*
* 結論:直接內存在直接的IO 操作上,在頻繁的讀寫時 會有顯著的性能提升
*
*/
public static void operateCompare(){
int time = 1000000000;
ByteBuffer buffer = ByteBuffer.allocate(2*time);
long st = System.currentTimeMillis();
for (int i = 0; i < time; i++) {
// putChar(char value) 用來寫入 char 值的相對 put 方法
buffer.putChar('a');
}
buffer.flip();
for (int i = 0; i < time; i++) {
buffer.getChar();
}
long et = System.currentTimeMillis();
System.out.println("在進行"+time+"次讀寫操作時,非直接內存讀寫耗時:" + (et-st) +"ms");
ByteBuffer buffer_d = ByteBuffer.allocateDirect(2*time);
long st_direct = System.currentTimeMillis();
for (int i = 0; i < time; i++) {
// putChar(char value) 用來寫入 char 值的相對 put 方法
buffer_d.putChar('a');
}
buffer_d.flip();
for (int i = 0; i < time; i++) {
buffer_d.getChar();
}
long et_direct = System.currentTimeMillis();
System.out.println("在進行"+time+"次讀寫操作時,直接內存讀寫耗時:" + (et_direct - st_direct) +"ms");
}
}
輸出:
在進行10000000次分配操作時,堆內存 分配耗時:12ms
在進行10000000次分配操作時,直接內存 分配耗時:8233ms
在進行1000000000次讀寫操作時,非直接內存讀寫耗時:4055ms
在進行1000000000次讀寫操作時,直接內存讀寫耗時:745ms
可以自己設置不同的time 值進行比較
分析
從數據流的角度,來看
非直接內存作用鏈:
本地IO –>直接內存–>非直接內存–>直接內存–>本地IO
直接內存作用鏈:
本地IO–>直接內存–>本地IO
直接內存使用場景
- 有很大的數據需要存儲,它的生命週期很長
- 適合頻繁的IO操作,例如網絡併發場景
參考
《深入理解Java虛擬機》 –周志明