单字节拷贝慢是因为每字节触发一次系统调用和上下文切换,500MB文件需约5.2亿次调用;缓冲区推荐8KB(8192),兼顾吞吐与内存占用,须用read()返回值控制write()写入长度,避免脏数据。

为什么单字节拷贝慢得反常?
用 FileInputStream.read() 逐字节读、FileOutputStream.write(int) 逐字节写,看起来逻辑清晰,但实际执行时,每读/写一个字节就触发一次系统调用 + 磁盘寻道(哪怕数据已在页缓存中)。对一个 500MB 文件,就是约 5.2 亿次函数调用 —— CPU 大量时间花在上下文切换和内核态/用户态跳转上,而不是搬运数据。
缓冲数组大小设成多少才合适?
不是越大越好,也不是固定写死 8192 就万能。关键看三件事:
-
read(byte[])返回值是实际读到的字节数,可能小于数组长度(尤其是文件末尾),必须用这个返回值控制write(byte[], int, int)的第三个参数,否则会写出脏数据 - 实测表明,
8192(8KB)在多数 HDD/SSD + JVM 默认堆配置下是吞吐与内存占用的较好平衡点;超过 64KB 后提升极小,还可能挤占其他对象空间 - 若目标是低延迟场景(如实时日志切片),可尝试
4096;若确定是顺序大文件且内存充裕,65536可再压榨 5%~10% 吞吐,但需配合-XX:+UseLargePages才稳定生效
必须用 try-with-resources 吗?
必须。手动调用 close() 容易遗漏或被异常中断,导致文件句柄泄漏 —— 表现为后续复制报 java.io.IOException: Too many open files。而 try (FileInputStream in = new FileInputStream(src); FileOutputStream out = new FileOutputStream(dst)) { ... } 能保证无论是否抛异常,流都会被关闭。
注意:缓冲数组本身不用 close,它只是普通 byte[],不持有系统资源。
缓冲区拷贝比 BufferedInputStream 快吗?
在纯顺序大文件场景下,二者性能几乎一致 —— 因为 BufferedInputStream 底层也是用 8KB 数组做缓冲,区别只在封装层级。但直接用 byte[] 缓冲有两点优势:
- 少一层对象包装,GC 压力略低(尤其高频小文件批量拷贝时)
- 可灵活控制读写节奏,比如遇到特定字节序列提前终止,或边读边校验 CRC,而
BufferedInputStream.read()是黑盒行为,无法干预内部缓冲策略
不过,如果需要链式处理(比如解密 → 解压 → 写入),用 BufferedInputStream 更易组合;单纯拷贝,byte[] 更轻量、更可控。
read() 返回值的配合是最容易出错的地方,很多人直接传 buffer.length 给 write(),结果末尾填了大量 0x00,文件虽能打开但内容损坏。

















