我创建了一个包含 1,000,000 个零的文件 (980K),并测量使用默认缓冲区大小(8192 字节)将其复制到另一个文件(对输入和输出使用缓冲和非缓冲)的速度。令人惊讶的是,缓冲输入和输出显着提高了性能。这就是结果
虽然我知道读取超过缓冲区大小(8KB)的大文件可能会抵消缓冲的好处,但我不明白为什么使用缓冲区的性能仍然比不使用缓冲区更好(至少需要更少的系统调用)。我在这里看到的唯一缺点似乎是缓冲区本身不必要的内存使用。
问题是: 1.除了在内存受限的环境中内存使用是一个问题之外,为什么我们不总是对输入和输出都使用缓冲区?
- 为什么缓冲输出时复制文件会更快?
我的代码包括 t1()、t2()、t3() 和 t4() 等函数,我不会在这里完整分享它们,因为它们与提供的代码类似:
static void t2() {
System.out.println("Copy file using BufferedInputStream (8192), with BufferOut");
long startTime = System.nanoTime();
try (BufferedInputStream br = new BufferedInputStream(new FileInputStream("text.txt"));
var bOut = new BufferedOutputStream(new FileOutputStream("text_3.txt"))) {
int i;
while ((i = br.read()) != -1) {
bOut.write(i);
}
} catch (IOException e) {
System.out.println(e);
}
long endTime = System.nanoTime();
long duration = (endTime - startTime) / 1000000;
System.out.println("Duration: " + duration + " millisecond");
}
我尝试过使用in.transferTo
缓冲输入和输出,结果更好(1 位数毫秒)。我还计划尝试不同的缓冲区大小。我读到最佳大小取决于文件系统块大小,在我的例子中是 4,096 字节。我应该将缓冲区大小设置为 4,096,还是越大越好?
这是低级操作系统 I/O。即使在 Java 中按字节写入时,也可能存在一些非 Java 内存缓冲区。
使用非缓冲 java I/O 将会有大量从 java 到操作系统级别的调用,然后偶尔传输缓冲区。这是非常昂贵的。
使用缓冲的 java I/O 可以更快地在一次本机调用中将 java 缓冲区复制到操作系统级别,然后执行实际的 I/O(假设缓冲区大小相同)。
你是对的,这需要一些 java 缓冲区的内存。
对于缓冲 I/O 和立即非缓冲 I/O,还有第三种替代方案。这使用内存映射文件。它直接在操作系统级缓冲区上运行(创建、读取、写入)。
MappedByteBuffer
。搜索一下如何使用FileChannel
andByteBuffer
,代码其实并不难写。现在看来你没有提到伟大的
Files
班级。使用更通用的Path
代替File
可以轻松完成所有这些 I/OnewByteChannel
:.聚苯乙烯
应该提到
long InputStream#transferTo(OutputStream)
(它可以异步工作,输入和输出之间不会有太多阻塞),但也Files.copy
存在。我收集是为了理解您使用更基本编码的底层机制。