代码之家  ›  专栏  ›  技术社区  ›  Keshav

Java NIO FileChannel与FileOutputstream的性能/实用性

  •  162
  • Keshav  · 技术社区  · 16 年前

    FileChannel FileInputStream/FileOuputStream 这条路比较慢。我可以知道更多的细节比较这两种方法。这是我使用的代码,我正在测试的文件就在这里 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 回复  |  直到 12 年前
        1
  •  208
  •   Stu Thompson Helter Scelter    10 年前

    我对较大文件的经验是 java.nio 快于 java.io 速度确实更快。 就像在>250%范围。这就是说,我正在消除明显的瓶颈,我建议您的微基准可能会受到这些瓶颈的影响。可能需要调查的领域:

    缓冲区大小。

    • 从磁盘复制到缓冲区
    • 从缓冲区复制到磁盘

    我自己的经验是,这个缓冲区大小是 用于调谐。我已经确定应用程序的一部分为4KB,另一部分为256KB。我怀疑你的代码有这么大的缓冲区。使用1KB、2KB、4KB、8KB、16KB、32KB和64KB的缓冲区运行一些基准测试,以向自己证明这一点。

    如果您这样做了,那么您实际上是在测试磁盘,而不是Java。我还建议,如果您的CPU不忙,那么您可能会遇到其他瓶颈。

    如果目标是另一个磁盘或NIC,为什么要复制到内存?对于较大的文件,所引起的延迟是非常重要的。

    正如其他人所说,使用 FileChannel.transferTo() 或 FileChannel.transferFrom() Direct Memory Access (这取决于实现,但通用CPU上的现代Sun和IBM版本很好用。) 发生的事情是数据直接进入/从光盘,到总线,然后到目的地。。。通过RAM或CPU绕过任何电路。

    使用生产数据和环境

    微基准容易失真。如果可以的话,尽可能在预期的硬件上以预期的负载准确地从计划中收集数据。

    我的基准测试是可靠的,因为它们发生在一个生产系统上,一个健壮的系统,一个负载下的系统,收集在日志中。 我的笔记本电脑是7200 RPM 2.5“SATA驱动器,而我则在密切注视JVM在硬盘上运行。

        2
  •  39
  •   Jared Burrows    12 年前

    如果要比较的是文件复制的性能,那么对于通道测试,您应该执行以下操作:

    final FileInputStream inputStream = new FileInputStream(src);
    final FileOutputStream outputStream = new FileOutputStream(dest);
    final FileChannel inChannel = inputStream.getChannel();
    final FileChannel outChannel = outputStream.getChannel();
    inChannel.transferTo(0, inChannel.size(), outChannel);
    inChannel.close();
    outChannel.close();
    inputStream.close();
    outputStream.close();
    

    许多操作系统可以直接将字节从文件系统缓存传输到目标通道,而无需实际复制它们。

        3
  •  7
  •   Community Mohan Dere    9 年前

    • 复制1000x2MB

      1. NIO(转移自)~2300ms
      2. NIO(直接数据缓冲器5000b翻转)~3500ms
      3. 标准IO(缓冲器5000b)~6000ms
      1. NIO(直接数据缓冲器5000b翻转)~4000ms
      2. NIO(转出)~5000ms
      3. 标准IO(缓冲器5000b)~6500ms
    • 复制1x1000mb

      1. 标准IO(缓冲器5000b)~7000ms

    transferTo()方法处理文件的块;不打算用作高级文件复制方法: How to copy a large file in Windows XP?

        4
  •  7
  •   antak Jason    9 年前

    回答问题的“有用性”部分:

    一个相当微妙的使用技巧 FileChannel FileOutputStream 是否执行任何阻塞操作(例如。 read() write() )从一根在里面的线 interrupted state 将导致通道突然关闭 java.nio.channels.ClosedByInterruptException .

    现在,这可能是一件好事 文件通道 是线程主要功能的一部分,设计时考虑了这一点。

    不幸的是,这太微妙了,因为不考虑这一点可能会导致影响写完整性的错误。 [1][2]

        5
  •  3
  •   Jörn Horstmann    16 年前

    FileChannel在以前的jvm版本中可能具有优势,因为在几个io相关的类中存在同步开销,但现代jvm非常擅长移除不需要的锁。

        6
  •  2
  •   eckes    13 年前

    如果您没有使用transferTo功能或非阻塞功能,您将不会注意到传统IO和NIO之间的区别(2),因为传统IO映射到NIO。

        7
  •  0
  •   tangens    16 年前

    我的经验是,NIO处理小文件的速度要快得多。但是当涉及到大文件时,FileInputStream/FileOutputStream要快得多。

    推荐文章