Windows 檔案複製速度似乎有個「天花板」。本文通過實測對比系統複製與 Java 緩衝流複製,揭示硬體 I/O 才是真正瓶頸,並分析 BufferedInputStream 預設 8KB 緩衝區的設計考量。
public void copyByte(String srcFile, String destFile) throws IOException {
InputStream in = new FileInputStream(srcFile);
OutputStream out = new FileOutputStream(destFile);
byte[] buf = new byte[16384]; // 16KB
int len;
System.out.println("開始複製....");
System.out.println(System.currentTimeMillis());
while ((len = in.read(buf)) != -1) {
out.write(buf, 0, len); // 關鍵:寫入實際讀取長度
}
System.out.println(System.currentTimeMillis());
System.out.println("複製結束....");
in.close();
out.close();
}
修正提示:原文 out.write(buf) 會寫入整個緩衝區導致檔案損壞,必須用 out.write(buf, 0, len)。
| 方式 | 緩衝區 | 耗時 | 速度 | 結論 |
|---|---|---|---|---|
| Windows 複製貼上 | 系統自動 | ~23 秒 | ~40 MB/s | 基準線 |
| Java 緩衝流 | 16 KB | ~23 秒 | ~40 MB/s | 達到硬體上限 |
| Java 緩衝流 | 8 KB | ~29 秒 | ~30 MB/s | 明顯下降 |
| Java 緩衝流 | >16 KB | ~23 秒 | ~40 MB/s | 不再提升 |
private static int defaultBufferSize = 8192; // 8KB
JDK 選擇 8KB 作為預設值,是基於以下權衡:
try (FileChannel in = FileChannel.open(Paths.get(src), StandardOpenOption.READ);
FileChannel out = FileChannel.open(Paths.get(dest), StandardOpenOption.WRITE, StandardOpenOption.CREATE)) {
in.transferTo(0, in.size(), out); // OS 級零拷貝,避免用戶態/核心態切換
}
ByteBuffer buffer = ByteBuffer.allocateDirect(1024 * 1024); // 1MB 直接緩衝區
// 配合 FileChannel.read/write 使用
BufferedInputStream 8KB 預設值兼顧通用性,高效能檔案複製建議手動調大至 16KB-64KB 或改用 NIO transferTo。dd if=/dev/zero of=test bs=1M count=1024 測磁碟速度,再怪代碼。