處理 GB 級大文件時,傳統 FileInputStream、RandomAccessFile 頻繁磁盤 I/O 導致效能低下。Java NIO 提供的記憶體映射文件將文件直接映射到進程地址空間,讀寫操作轉為記憶體操作,效能提升數十倍。本文通過基準測試對比三種讀取方式,並給出生產級封裝範例。
| 方式 | 含業務邏輯 | 純讀取 |
|---|---|---|
| FileInputStream | 1464 ms | 1458 ms |
| BufferedInputStream | 72 ms | 67 ms |
| MappedByteBuffer | 25 ms | 8 ms |
結論:映射文件快 50-180 倍,且文件越大優勢越明顯(OS 自動換頁管理)。
// 獲取文件通道
FileChannel channel = new RandomAccessFile(file, "rw").getChannel();
// 映射模式:READ_ONLY / READ_WRITE / PRIVATE
MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, fileSize);
// 讀寫操作(ByteBuffer 所有方法均可用)
byte b = buffer.get(index); // 讀取單字節
buffer.put(index, (byte) value); // 寫入單字節
buffer.force(); // 強制刷新到磁盤
buffer.load(); // 預加載到物理記憶體
public class LargeFileProcessor {
public static void processLineByLine(File file, LineProcessor processor) throws IOException {
try (FileChannel channel = FileChannel.open(file.toPath(), StandardOpenOption.READ)) {
long fileSize = channel.size();
MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, fileSize);
int start = 0;
for (int i = 0; i < fileSize; i++) {
if (buffer.get(i) == '\n') {
int length = i - start;
byte[] lineBytes = new byte[length];
buffer.position(start).get(lineBytes);
processor.process(new String(lineBytes, StandardCharsets.UTF_8));
start = i + 1;
}
}
// 處理最後一行(無換行符)
if (start < fileSize) {
byte[] lineBytes = new byte[fileSize - start];
buffer.position(start).get(lineBytes);
processor.process(new String(lineBytes, StandardCharsets.UTF_8));
}
}
}
@FunctionalInterface
public interface LineProcessor {
void process(String line);
}
}
buffer.force() 確保持久化PRIVATE 模式(Copy-on-Write)FileChannel.close() 會取消映射,配合 try-with-resources 使用記憶體映射文件是處理大文件的利器,將 I/O 瓶頸轉移給 OS 頁面管理系統。適用場景:日誌分析、大數據導入、文件校驗、多媒體處理。掌握 MappedByteBuffer 與 FileChannel 組合,能讓你的文件處理代碼既簡潔又高效。