我试图弄清楚当我们使用nio FileChannel和normal 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"); } }
我对大型文件的体验java.nio比更快java.io。 确实更快。 例如在> 250%的范围内。也就是说,我正在消除明显的瓶颈,我认为这可能会影响你的微基准测试。调查的潜在领域:
java.nio
java.io
缓冲区大小。 你基本上拥有的算法是
我自己的经验是,此缓冲区大小已经可以调整。我为应用程序的一部分分配了4KB,为另一部分分配了256KB。我怀疑你的代码正在遭受如此大的缓冲区。使用1KB,2KB,4KB,8KB,16KB,32KB和64KB的缓冲区运行一些基准测试以向自己证明。
不要执行读写同一磁盘的Java基准测试。
如果这样做,那么你实际上是在对磁盘进行基准测试,而不是对Java进行基准测试。我还建议,如果你的CPU不忙,那么你可能会遇到其他瓶颈。
如果不需要,请不要使用缓冲区。
如果目标是其他磁盘或NIC,为什么还要复制到内存中?对于较大的文件,确保的延迟是不平凡的。
就像其他人所说的那样,请使用FileChannel.transferTo()或FileChannel.transferFrom()。此处的主要优点是,JVM使用操作系统对DMA(直接内存访问)的访问(如果存在)。(这取决于实现方式,但是可以在通用CPU上使用现代的Sun和IBM版本。) 发生的情况是数据直接往返于磁盘,总线,然后直接到达目标……绕过任何电路RAM或CPU。
FileChannel.transferTo()
FileChannel.transferFrom()
我日夜不停地工作的Web应用程序非常繁忙。我也做了微观基准和现实基准。结果在我的博客上,看看:
使用生产数据和环境
微观基准易于失真。如果可以,请根据预期的负载,在预期的硬件上,按照计划要做的事情来收集数据。
我的基准是可靠且可靠的,因为它们是在生产系统,强大的系统,有负载的系统以及日志中收集的。 而不是我笔记本的7200 RPM 2.5英寸SATA驱动器,而当我密切关注JVM在硬盘上工作时。
你在做什么?这很重要。