bufferedoutputstream通过内存缓冲区聚合写入请求,数据先存入字节数组(默认8kb),仅在填满、调用flush()或close()时批量触发一次系统写调用;避免循环flush、漏close或冗余包装,推荐try-with-resources和按场景调优缓冲大小。

BufferedOutputStream 通过在内存中维护一块缓冲区,把多次小写操作“攒起来”,等凑够一定量或触发特定条件时再统一落盘,从而显著减少磁盘 I/O 次数。
缓冲区怎么拦截并聚合写入请求
每次调用 write(int) 或 write(byte[]),数据并不立刻写入磁盘,而是先复制到内部的字节数组缓冲区里。只有以下情况之一发生时,缓冲区内容才会真正交给底层流(比如 FileOutputStream)执行一次系统写调用:
- 缓冲区被填满(默认大小是 8192 字节,即 8KB)
- 显式调用 flush()
- 调用 close()(内部会先 flush 再关闭)
不加缓冲 vs 加了 BufferedOutputStream 的效果对比
假设要写 100 个字节,每次只写 1 字节:
- 直接用 FileOutputStream:大概率触发 100 次系统 write 调用,频繁访问磁盘
- 套上 BufferedOutputStream:数据先进缓冲区,100 字节很可能一次就刷出去,仅 1 次真实写入
这种聚合能力对日志逐行写、序列化字段、生成配置文件等高频小写场景特别有效。
关键细节和常见踩坑点
缓冲区虽好,但用错反而拖累性能甚至丢数据:
- 每写 1 字节就 flush() 一次:等于主动禁用缓冲,I/O 次数不减反增,还多出方法调用开销
- 忘记 close() 或没在关键节点 flush():程序结束前数据还卡在内存缓冲区,文件内容不完整
- 在 GZIP 压缩场景下盲目套一层 BufferedOutputStream:GZIPOutputStream 本身已有压缩缓冲,冗余包装会增加内存拷贝和 CPU 开销,可能加剧抖动
怎么让缓冲真正起作用
核心是控制“何时真正写盘”:
- 写入量接近缓冲区容量(如批量写 4KB~32KB)再 flush,比单字节循环更高效
- 按逻辑单元组织写入——比如一条完整日志、一个对象序列化结果写完再 flush
- 优先用 try-with-resources 自动 close,确保最后一定会 flush + 关闭
- 需要自定义性能时,可通过构造函数传入更大的缓冲区(如 new BufferedOutputStream(fos, 65536))
本质上,它不是消除磁盘 I/O,而是把零散的小请求合并成更少、更大的请求,让 JVM 层面对 I/O 的控制更可控、更高效。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











