我试图弄清楚当我们使用nio FileChannel
和普通的FileInputStream/FileOuputStream
来读写文件系统时,在性能(或优势)上是否有任何差异。我观察到,在我的机器上,两者的性能都在同一水平上,而且很多时候FileChannel
的方式更慢。我能知道更多的细节比较这两种方法。下面是我使用的代码,我正在测试的文件大约是350MB
。如果我不考虑随机访问或其他类似的高级功能,那么使用基于NIO的类进行文件I/O是一个好的选择吗?
package trialjavaprograms;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
public class JavaNIOTest {
public static void main(String[] args) throws Exception {
useNormalIO();
useFileChannel();
}
private static void useNormalIO() throws Exception {
File file = new File("/home/developer/test.iso");
File oFile = new File("/home/developer/test2");
long time1 = System.currentTimeMillis();
InputStream is = new FileInputStream(file);
FileOutputStream fos = new FileOutputStream(oFile);
byte[] buf = new byte[64 * 1024];
int len = 0;
while((len = is.read(buf)) != -1) {
fos.write(buf, 0, len);
}
fos.flush();
fos.close();
is.close();
long time2 = System.currentTimeMillis();
System.out.println("Time taken: "+(time2-time1)+" ms");
}
private static void useFileChannel() throws Exception {
File file = new File("/home/developer/test.iso");
File oFile = new File("/home/developer/test2");
long time1 = System.currentTimeMillis();
FileInputStream is = new FileInputStream(file);
FileOutputStream fos = new FileOutputStream(oFile);
FileChannel f = is.getChannel();
FileChannel f2 = fos.getChannel();
ByteBuffer buf = ByteBuffer.allocateDirect(64 * 1024);
long len = 0;
while((len = f.read(buf)) != -1) {
buf.flip();
f2.write(buf);
buf.clear();
}
f2.close();
f.close();
long time2 = System.currentTimeMillis();
System.out.println("Time taken: "+(time2-time1)+" ms");
}
}
7条答案
按热度按时间hrysbysz1#
我对较大文件大小的经验是
java.nio
比java.io
快。* 速度快得多。* 在>250%的范围内。也就是说,我正在消除明显的瓶颈,我建议您的微基准可能会受到影响。可能的调查领域:**缓冲区大小。**您基本上拥有的算法是
我自己的经验是,这个缓冲区大小已经 * 成熟**了。我将应用程序的一部分设置为4KB,另一部分设置为256 KB。我怀疑你的代码正在遭受如此大的缓冲区。使用1 KB、2KB、4KB、8 KB、16 KB、32 KB和64 KB的缓冲区运行一些基准测试来证明这一点。
不要执行对同一个磁盘进行读写的java基准测试。
如果你这样做,那么你实际上是在对磁盘进行基准测试,而不是Java。我还建议,如果你的CPU不忙碌,那么你可能正在经历一些其他的瓶颈。
如果不需要,请不要使用缓冲区。
如果目标是另一个磁盘或NIC,为什么要复制到内存?对于较大的文件,延迟是不平凡的。
就像其他人说的,使用
FileChannel.transferTo()
或FileChannel.transferFrom()
。这里的主要优点是JVM使用操作系统对DMA(Direct Memory Access)的访问(如果存在的话)。* (这取决于实现,但现代Sun和IBM版本的通用CPU是好的。)* 发生的是数据直接进入/从磁盘,到总线,然后到目的地.绕过任何通过RAM或CPU的电路。我日夜工作的Web应用程序非常IO繁重。我也做过微观基准测试和真实世界的基准测试。结果在我的博客上,看看吧:
使用生产数据和环境
微观基准容易失真。如果可以的话,请努力从您计划执行的操作中收集数据,并在您预期的硬件上使用您预期的负载。
我的基准测试是可靠的,因为它们发生在一个生产系统上,一个健壮的系统,一个负载下的系统,收集在日志中。* 不是 * 我的笔记本电脑的7200 RPM 2.5”SATA驱动器,而我密切关注JVM工作我的硬盘。
你在跑什么?这很重要
qkf9rpyu2#
如果你想比较的是文件复制的性能,那么对于通道测试,你应该这样做:
这不会比你自己从一个频道缓冲到另一个频道慢,而且可能会快得多。根据Javadoc的说法:
许多操作系统可以直接将字节从文件系统缓存传输到目标通道,而无需实际复制它们。
hfyxw5xn3#
回答问题的“有用性”部分:
在
FileOutputStream
上使用FileChannel
的一个相当微妙的问题是,执行任何阻塞操作(例如,read()
或write()
)将导致通道突然关闭,并显示java.nio.channels.ClosedByInterruptException
。现在,如果
FileChannel
的用途是线程主函数的一部分,并且设计考虑到了这一点,这可能是一件好事。但如果它被一些辅助功能(如日志功能)使用,也会很麻烦。例如,如果日志记录函数碰巧被一个也被中断的线程调用,您可能会发现日志记录输出突然关闭。
不幸的是,这是如此微妙,因为不考虑这可能会导致影响写入完整性的错误。
tvmytwxo4#
根据我的测试(Win7 64位,6 GB RAM,Java 6),NIO transferFrom只对小文件快速,对大文件则非常慢。NIO数据缓冲器翻转始终优于标准IO。
1.标准IO(缓冲5000 b)~ 6000 ms
1.标准IO(缓冲5000 b)~ 6500 ms
1.标准IO(缓冲5000 b)~ 7000 ms
transferTo()方法作用于文件的块;并不打算作为高级文件复制方法:如何在Windows XP中复制大文件?
kxkpmulp5#
我测试了FileInputStream与FileChannel用于解码base64编码的文件。在我的经验中,我测试了相当大的文件,传统的io总是比nio快一点。
FileChannel在jvm的早期版本中可能有优势,因为几个io相关类的同步开销,但现代jvm在删除不需要的锁方面非常出色。
vcirk6k66#
如果你没有使用transferTo特性或非阻塞特性,你就不会注意到传统IO和NIO之间的区别(2),因为传统IOMap到NIO。
但是如果你可以使用NIO的特性,比如transferFrom/To或者想使用Buffers,那么NIO当然是最好的选择。
(注意,在某些情况下,传统的阻塞IO表现得更好,其中一些是由bug引起的,另一些是因为开销较少或不同的缓冲区设置,所以比较总是好的)。
klsxnrf17#
我的经验是,NIO在小文件上要快得多。但是对于大文件来说,FileInputStream/FileOutputStream要快得多。