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

Kes*_*hav 165 java optimization operations nio file

我试图弄清楚当我们使用nio FileChannel与普通FileInputStream/FileOuputStream文件读取和写入文件系统时,性能(或优势)是否有任何差异.我观察到,在我的机器上,两者都在同一级别执行,也很多次FileChannel都是慢一些.我可以了解比较这两种方法的更多细节.这是我使用的代码,我正在测试的文件是350MB.如果我不是在查看随机访问或其他此类高级功能,那么对文件I/O使用基于NIO的类是一个不错的选择吗?

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");
    }
}
Run Code Online (Sandbox Code Playgroud)

Stu*_*son 197

我对较大文件大小的体验java.nio比快java.io. 坚固得快. 比如> 250%的范围.也就是说,我正在消除明显的瓶颈,我建议你的微基准可能会受到影响.潜在的调查领域:

缓冲区大小. 你基本上拥有的算法是

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

我自己的经验是,这个缓冲区的大小是成熟的调整.我已经为我的应用程序的一部分确定了4KB,另一部分支持了256KB.我怀疑你的代码遭受如此大的缓冲.使用1KB,2KB,4KB,8KB,16KB,32KB和64KB的缓冲区运行一些基准来证明这一点.

不要执行读取和写入同一磁盘的java基准测试.

如果你这样做,那么你真的是对磁盘进行基准测试,而不是Java.我还建议,如果您的CPU不忙,那么您可能遇到了其他一些瓶颈.

如果您不需要,请不要使用缓冲区.

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

像其他人说的那样,使用FileChannel.transferTo()FileChannel.transferFrom().这里的关键优势是JVM使用操作系统访问DMA(直接内存访问)(如果存在).(这是依赖于实现的,但是通用CPU上的现代Sun和IBM版本很好用.) 会发生什么事情是数据直接进出光盘,到达总线,再到目的地......绕过任何电路RAM或CPU.

我花了几天时间工作的网络应用程序非常重要.我也做过微基准测试和实际基准测试.结果出现在我的博客上,看看:

使用生产数据和环境

微基准测试很容易失真.如果可以的话,尽可能地在您期望的硬件上使用您期望的负载来准确地收集您计划执行的操作的数据.

我的基准测试结果坚实可靠,因为它们发生在生产系统,强大系统,负载系统,日志中. 不是我的笔记本电脑的7200转2.5英寸SATA驱动器,当我看到JVM工作我的硬盘时非常强烈.

你在跑什么?这很重要.

  • 以下是archive.org上的博客链接:http://web.archive.org/web/20120815094827/http://geekomatic.ch/2008/09.html http://web.archive.org/web/20120821114802 /http://geekomatic.ch/2009/01.html (14认同)

uck*_*man 38

如果要比较的是文件复制的性能,那么对于通道测试,你应该这样做:

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();
Run Code Online (Sandbox Code Playgroud)

这不会比从一个通道缓冲到另一个通道慢,并且可能会大大加快.根据Javadocs:

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


nox*_*oxo 7

基于我的测试(Win7 64位,6GB RAM,Java6),NIO transferFrom仅对小文件很快,在较大文件上变得非常慢.NIO数据缓冲器翻转总是优于标准IO.

  • 复制1000x2MB

    1. NIO(transferFrom)~2300ms
    2. NIO(直接数据缓冲5000b翻转)~3500ms
    3. 标准IO(缓冲5000b)~6000ms
  • 复制100x20mb

    1. NIO(直接数据缓冲5000b翻转)~4000ms
    2. NIO(transferFrom)~5000ms
    3. 标准IO(缓冲区5000b)~6500ms
  • 复制1x1000mb

    1. NIO(直接datababuffer 5000b翻转)~4500s
    2. 标准IO(缓冲5000b)~7000ms
    3. NIO(transferFrom)~8000ms

transferTo()方法适用于文件的块; 不是作为高级文件复制方法: 如何在Windows XP中复制大文件?


ant*_*tak 5

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

使用FileChannelover的一个相当微妙的陷阱FileOutputStream是,从处于中断状态的线程执行其任何阻塞操作(例如read()write())将导致通道突然关闭。java.nio.channels.ClosedByInterruptException

现在,如果将FileChannel使用的对象作为线程主要功能的一部分,则这可能是一件好事,并且在设计时已将其考虑在内。

但是,如果由某些辅助功能(例如记录功能)使用它,也可能会令人讨厌。例如,如果日志记录函数碰巧被也被中断的线程调用,您会发现日志记录输出突然关闭。

不幸的是,它是如此微妙,因为不考虑这一点会导致影响写入完整性的错误。[1] [2]