filewriter.flush() 经常没效果是因为它仅将java缓冲区数据写入os内核缓冲区,不保证落盘;真正落盘需调用filewriter.getfd().sync()强制同步到磁盘。

FileWriter.flush() 为什么经常没效果?
调用 flush() 并不能保证数据真正落盘,它只把 Java 层缓冲区内容写入操作系统内核缓冲区(page cache),后续由 OS 异步刷盘。这是最常被误解的一点——很多人以为 flush() 就等于“已保存到磁盘”,结果进程崩溃或断电时数据丢失。
FileWriter.flush() 和 FileOutputStream.getFD().sync() 的区别
flush() 是 Writer 层的刷新,仅清空 Java 自己的字符缓冲;要强制同步到磁盘,必须穿透到底层文件描述符。关键操作是:fileWriter.getFD().sync()(注意:getFD() 返回的是 java.io.FileDescriptor)。
常见错误写法:
fileWriter.flush(); // ✅ 刷到内核缓冲区 // ❌ 缺少 sync,不保证落盘
正确做法:
fileWriter.flush(); fileWriter.getFD().sync(); // ✅ 强制内核缓冲区同步到磁盘
-
sync()是阻塞调用,会等到物理设备确认写入完成(如 SSD/NVMe 的 write confirm),耗时可能达毫秒级 - 不是所有文件系统都支持
sync()(例如某些网络文件系统会静默忽略),建议配合try-catch捕获IOException -
FileWriter构造时若未显式禁用缓冲(它默认带 8192 字节缓冲),flush()前的写入可能还在 Java 缓冲里,所以顺序不能颠倒
替代方案:用 FileOutputStream + OutputStreamWriter 更可控
FileWriter 是 OutputStreamWriter 的封装,但隐藏了底层 OutputStream,导致 getFD() 调用略绕。更清晰的做法是直接使用 FileOutputStream:
FileOutputStream fos = new FileOutputStream("log.txt", true);
OutputStreamWriter osw = new OutputStreamWriter(fos, StandardCharsets.UTF_8);
osw.write("data\n");
osw.flush();
fos.getFD().sync(); // 直接从 fos 获取,语义更明确
- 避免
FileWriter的编码黑盒(它默认用平台编码,容易乱码) - 可以复用同一个
FileOutputStream实例多次sync(),而FileWriter每次构造都会新建流 - 如果需要高性能日志,考虑用
FileChannel.force(true)替代sync(),行为等价但更符合 NIO 风格
Windows 下 flush() + sync() 仍丢数据?检查文件打开标志
在 Windows 上,即使调用了 sync(),若文件是以 FILE_FLAG_WRITE_THROUGH 以外的方式打开(Java 默认不设该标志),部分 SSD 固件可能仍缓存写入。这不是 Java 层能控制的,但可规避:
- 确保没有其他进程以
FILE_SHARE_WRITE方式同时打开同一文件(竞争写入干扰 sync 语义) - 测试时用
ProcessBuilder执行fsutil behavior query disablelastaccess确认系统未禁用时间戳更新(间接反映底层写策略) - 生产环境若对持久性要求极高(如金融交易日志),应改用
RandomAccessFile或 JNI 调用FlushFileBuffers
真正难的不是调用哪个方法,而是理解「Java 缓冲 → OS 缓冲 → 设备缓存」这三层异步模型。漏掉任意一层,flush() 都只是假动作。










