java 9中bufferedinputstream的transferto方法会复用其内部缓冲区,但需满足已初始化且未关闭的前提,仍使用固定大小缓冲区并受底层流影响,非全程零拷贝。

Java 9 引入的 InputStream.transferTo(OutputStream) 方法在 BufferedInputStream 中确实会复用其内部缓冲区,但这种复用有明确前提和边界,不是无条件的“全程零拷贝”或“无限复用”。
transferTo 的默认实现不依赖 BufferedInputStream 的缓冲区
所有 InputStream 子类(包括 BufferedInputStream)继承自父类的 transferTo 默认实现,该实现使用一个固定大小(8192 字节)的临时字节数组作为缓冲区,每次读取后写入目标流。这个数组是方法内局部分配的,与 BufferedInputStream 自身的缓冲区无关。
BufferedInputStream 重写了 transferTo 并主动复用自身缓冲区
从 Java 9 开始,BufferedInputStream 覆盖了 transferTo 方法,核心优化在于:它直接使用自己已分配的 buf 字段(即构造时或扩容后的内部字节数组)作为读取缓冲区,避免重复创建新数组。
- 复用前提是
BufferedInputStream已完成初始化(buf != null),且未被关闭 - 复用过程仍需调用
read()(内部走fill()逻辑),所以仍受底层流阻塞、返回字节数等影响 - 若底层流是
FileInputStream,JDK 还可能进一步触发FileChannel.transferTo的底层零拷贝(但这属于另一层优化,与BufferedInputStream缓冲区复用无关)
缓冲区复用的局限性与注意事项
即使复用了内部缓冲区,也不能认为整个传输过程完全免于内存拷贝或绝对高效:
- 缓冲区大小固定(默认 8192 字节),无法动态适配大文件或高吞吐场景;手动指定更大缓冲区(如
new BufferedInputStream(in, 65536))才能提升单次读取量 - 如果
BufferedInputStream当前缓冲区中已有未读数据(例如之前调用过read()导致pos ),<code>transferTo会先清空这部分“残留”数据,再进入常规填充-写入循环 - 多线程环境下,不能假设复用缓冲区带来并发安全增益——
transferTo方法本身不是同步的,仍需外部保证流的独占访问
如何验证缓冲区是否被复用
可通过反射或调试确认:BufferedInputStream 的 buf 字段在 transferTo 执行期间被直接传给 read(b, off, len) 调用,而非新建数组。例如断点进入其 transferTo 源码,可见类似逻辑:
这表明它确实在复用自身缓冲区,而非依赖父类的临时数组。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











