最近,我创建了一个界面,强制用户使用默认方法实现单个fromStream(OutputStream)
,如下所示:
public default T fromFile(File file) throws IOException {
try (InputStream stream = new FileInputStream(file)) {
return fromStream(stream);
}
}
由于单个字节直接从FileInputStream
读取,因此很快就发现这非常昂贵(每MB几秒)。
将其包裹在BufferedInputStream
解决了我的问题,但它让我想到为什么FileInputStream
非常昂贵。
文件通道在读取字节时不会关闭或打开,那么为什么首先需要缓冲区呢?
答案 0 :(得分:3)
如果使用read()
方法从无缓冲流中读取字节,JVM将最终向操作系统重复读取系统调用,以从文件中读取单个字节。 (在引擎盖下,JVM可能会将read(addr, offset, count)
调用为1。)
进行系统调用的成本很高。比常规方法调用至少高出几个数量级。这是因为在:
中存在很大的开销相比之下,如果您使用缓冲流,则流将尝试以大块的形式从操作系统中读取文件。这通常会导致系统调用数量减少数千倍。
实际上,这不是关于文件如何存储在磁盘上的。确实,数据最终必须一次读取一个块,等等。但是,操作系统足够聪明,可以自行缓冲。它甚至可以预读文件的一部分,以便它们在(内核)内存中为应用程序做好准备,同时让系统调用来读取它们。
多个单字节read()
调用极不可能导致额外的磁盘流量。唯一合适的情况是,如果您在每个read()
之间等待很长时间......并且操作系统会重用它缓存磁盘块的空间。
答案 1 :(得分:1)
当您从文件中读取时,您必须一次读取一个块,因为这是硬件支持的唯一数量。如果你在没有缓冲的情况下一次读取一个字符,那么假设512B块,你将读取相同的块512次以读取整个块。如果你读取并缓冲,你将访问磁盘,然后从内存中读取。
访问磁盘比访问内存慢几个数量级,这不是一个好主意。