處理 GB 級甚至更大檔案時,BufferedInputStream / BufferedReader 等傳統阻塞 I/O 存在明顯短板:
-Xmx 限制),無法一次載入超大檔案。read()/write() 系統呼叫帶來上下文切換開銷。MappedByteBuffer 透過作業系統的虛擬記憶體管理,將檔案直接映射到進程的虛擬位址空間(非物理記憶體)。核心優勢:
get(index) / put(index, val),OS 自動處理頁面換入換出。| 模式 | 常數 | 行為 |
|---|---|---|
| 唯讀 | MapMode.READ_ONLY | 嘗試修改拋出 ReadOnlyBufferException |
| 讀寫 | MapMode.READ_WRITE | 修改最終同步回磁碟,對其他映射同檔案的進程可見性不保證 |
| 私有 | MapMode.PRIVATE | 修改不寫回檔案,建立私有副本,其他進程不可見 |
// FileChannel 映射檔案區域
MappedByteBuffer map(FileChannel.MapMode mode, long position, long size)
// 強制刷新變更到磁碟 (READ_WRITE 模式有效)
mbb.force();
// 預載入內容到物理記憶體
mbb.load();
// 檢查是否駐留物理記憶體
boolean loaded = mbb.isLoaded();
public class MappedFileCopy {
public static void main(String[] args) throws Exception {
String src = "E:/data/large_file.dat"; // 1.4GB 測試檔
String dest = "E:/data/copy_mapped.dat";
// 傳統 NIO (Heap ByteBuffer)
long t1 = System.currentTimeMillis();
try (FileChannel in = new FileInputStream(src).getChannel();
FileChannel out = new FileOutputStream(dest).getChannel()) {
ByteBuffer buf = ByteBuffer.allocate(1024 * 1024); // 1MB 堆緩衝區
while (in.read(buf) != -1) {
buf.flip();
out.write(buf);
buf.clear();
}
}
System.out.println("Heap Buffer: " + (System.currentTimeMillis() - t1) + "ms");
// MappedByteBuffer (直接記憶體)
long t2 = System.currentTimeMillis();
try (FileChannel in = new FileInputStream(src).getChannel();
FileChannel out = new FileOutputStream(dest + "_mapped").getChannel()) {
MappedByteBuffer mbb = in.map(FileChannel.MapMode.READ_ONLY, 0, in.size());
out.write(mbb); // 一次性寫入,OS 自動分頁處理
}
System.out.println("MappedByteBuffer: " + (System.currentTimeMillis() - t2) + "ms");
}
}
典型結果:傳統堆緩衝區 ~2000ms,MappedByteBuffer ~50ms(視磁碟/OS 快取而定),效能提升 10-40 倍。
MappedByteBuffer 無 unmap() 方法,檔案鎖定直到 Buffer 被 GC 回收(Cleaner.clean()),時間不可控。導致:
OutOfMemoryError: Map failed)。--add-opens)public static void unmap(MappedByteBuffer buffer) throws Exception {
if (buffer == null || !buffer.isDirect()) return;
Method cleanerMethod = buffer.getClass().getMethod("cleaner");
cleanerMethod.setAccessible(true);
Object cleaner = cleanerMethod.invoke(buffer);
Method cleanMethod = cleaner.getClass().getMethod("clean");
cleanMethod.invoke(cleaner);
System.out.println("MappedByteBuffer 已釋放映射");
}
生產環境建議:
MemorySegment (Foreign Function & Memory API) 替代,支援確定性釋放。finally 區塊調用。掌握 MappedByteBuffer 的原理與坑點,是 Java 高效能 I/O 進階的必修課。