直接緩衝區和非緩衝區
直接緩衝區與非直接緩衝區別
非直接緩衝區:通過 allocate() 方法分配緩衝區,將緩衝區建立在 JVM 的記憶體中
上圖讀的過程: 讀物理磁碟檔案時候,先到實體記憶體讀到,然後拷貝到jvm記憶體中。 程式去jvm讀取。 從物理空間拷貝到jvm記憶體空間,效率慢。
寫的過程: 先寫到jvm記憶體,jvm拷貝到實體記憶體,然後再到物理磁碟。
非直接緩衝區 直接存放在 jvm緩衝區 需要來回copy
非直接緩衝區 存放在實體記憶體 不需要copy
存放在實體記憶體比jvm緩衝區效率高
直接緩衝區:通過 allocateDirect() 方法分配直接緩衝區,將緩衝區建立在實體記憶體中。可以提高效率
非直接緩衝區:
應用程式 不走jvm記憶體和實體記憶體。 走的是實體記憶體對映檔案。記憶體條
寫: 直接寫到實體記憶體對映檔案,到物理磁碟
讀: 物理磁碟檔案讀到實體記憶體對映檔案,然後讀取之
直接緩衝區 佔記憶體 不安全 但是效率高
位元組緩衝區要麼是直接的,要麼是非直接的。如果為直接位元組緩衝區,則 Java 虛擬機器會盡最大努力直接在此緩衝區上執行本機 I/O 操作。也就是說,在每次呼叫基礎作業系統的一個本機 I/O 操作之前(或之後),虛擬機器都會盡量避免將緩衝區的內容複製到中間緩衝區中(或從中間緩衝區中複製內容)。
直接位元組緩衝區可以通過呼叫此類的 allocateDirect() 工廠方法來建立。此方法返回的緩衝區進行分配和取消分配所需成本通常高於非直接緩衝區。直接緩衝區的內容可以駐留在常規的垃圾回收堆之外,因此,它們對應用程式的記憶體需求量造成的影響可能並不明顯。所以,建議將直接緩衝區主要分配給那些易受基礎系統的本機 I/O 操作影響的大型、持久的緩衝區。一般情況下,最好僅在直接緩衝區能在程式效能方面帶來明顯好處時分配它們。
直接位元組緩衝區還可以通過 FileChannel 的 map() 方法 將檔案區域直接對映到記憶體中來建立。該方法返回MappedByteBuffer 。 Java 平臺的實現有助於通過 JNI 從本機程式碼建立直接位元組緩衝區。如果以上這些緩衝區中的某個緩衝區例項指的是不可訪問的記憶體區域,則試圖訪問該區域不會更改該緩衝區的內容,並且將會在訪問期間或稍後的某個時間導致丟擲不確定的異常。
位元組緩衝區是直接緩衝區還是非直接緩衝區可通過呼叫其 isDirect() 方法來確定。提供此方法是為了能夠在效能關鍵型程式碼中執行顯式緩衝區管理。
IO緩衝區 非直接緩衝區
// 使用直接緩衝區完成檔案的複製(記憶體對映檔案) static public void test2() throws IOException { long start = System.currentTimeMillis(); FileChannel inChannel = FileChannel.open(Paths.get("f://1.mp4"), StandardOpenOption.READ); FileChannel outChannel = FileChannel.open(Paths.get("f://2.mp4"), StandardOpenOption.WRITE, StandardOpenOption.READ, StandardOpenOption.CREATE); // 記憶體對映檔案 MappedByteBuffer inMappedByteBuf = inChannel.map(MapMode.READ_ONLY, 0, inChannel.size()); MappedByteBuffer outMappedByteBuffer = outChannel.map(MapMode.READ_WRITE, 0, inChannel.size()); // 直接對緩衝區進行資料的讀寫操作 byte[] dsf = new byte[inMappedByteBuf.limit()]; inMappedByteBuf.get(dsf); outMappedByteBuffer.put(dsf); inChannel.close(); outChannel.close(); long end = System.currentTimeMillis(); System.out.println(end - start); } // 1.利用通道完成檔案的複製(非直接緩衝區) static public void test1() throws IOException { // 4400 long start = System.currentTimeMillis(); FileInputStream fis = new FileInputStream("f://1.mp4"); FileOutputStream fos = new FileOutputStream("f://2.mp4"); // ①獲取通道 FileChannel inChannel = fis.getChannel(); FileChannel outChannel = fos.getChannel(); // ②分配指定大小的緩衝區 ByteBuffer buf = ByteBuffer.allocate(1024); while (inChannel.read(buf) != -1) { buf.flip();// 切換為讀取資料 // ③將緩衝區中的資料寫入通道中 outChannel.write(buf); buf.clear(); } outChannel.close(); inChannel.close(); fos.close(); fis.close(); long end = System.currentTimeMillis(); System.out.println(end - start); }