4

有人可以解释一下该transferTo方法如何以看似 1000+ MB/秒的速度复制文件。我使用 372MB 的二进制文件运行了一些测试,第一个副本很慢,但是如果我更改输出名称并再次运行它,输出目录中会出现一个额外的文件,只需 180 毫秒,即超过 2000 MB/秒。这里发生了什么?我正在运行 Windows 7。

private static void doCopyNIO(String inFile, String outFile) {
    FileInputStream     fis = null;
    FileOutputStream    fos = null;
    FileChannel         cis = null;
    FileChannel         cos = null;

    long                len = 0, pos = 0;

    try {
        fis = new FileInputStream(inFile);
        cis = fis.getChannel();
        fos = new FileOutputStream(outFile);
        cos = fos.getChannel();
        len = cis.size();
        while (pos < len) {
            pos += cis.transferTo(pos, (1024 * 1024 * 10), cos);    // 10M
        }
        fos.flush();
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        if (cos != null) { try { cos.close(); } catch (Exception e) { } }
        if (fos != null) { try { fos.close(); } catch (Exception e) { } }
        if (cis != null) { try { cis.close(); } catch (Exception e) { } }
        if (fis != null) { try { fis.close(); } catch (Exception e) { } }
    }
}
4

3 回答 3

6

关键是“第一次”。您的操作系统已将整个文件缓存在 RAM 中(如今 372MB 并不多),因此唯一的开销是通过内存映射空间翻转零拷贝缓冲区所需的时间。如果您刷新缓存(不知道如何在 Windows 上执行此操作;如果文件在外部驱动器上,您可以拔下并重新插入),您会看到读取速率稳定下来,并且如果您强制操作系统刷新写入,如果您有硬盘,您的程序将阻塞 10 秒左右。

于 2013-09-04T20:07:39.107 回答
2

我猜一旦文件被读取一次,操作系统就会缓存它以加快后续读取速度。此外,NTFS 中称为单实例存储的功能也可能发挥作用,如 Wikipedia 中所述:

当有多个目录具有不同但相似的文件时,其中一些文件可能具有相同的内容。单实例存储允许将相同的文件合并到一个文件并创建对该合并文件的引用。

https://en.wikipedia.org/wiki/NTFS#Single_Instance_Storage_.28SIS.29

我不确定这是否是您所看到的,但这是我能想到的唯一有意义的事情。

于 2013-09-04T20:09:55.640 回答
1

就缓冲 IO 性能而言,这似乎是正确的……发生的事情是您仅将文件读取和写入内存,然后在后台,操作系统将输出文件“刷新”到磁盘。您没有测量将文件写入磁盘所需的时间,而只是写入内存。

当您使用新的 Java7 Files.newOutputStream(...)DSYNC OpenOption打开 FileOutputStreams 时,您可能需要再次尝试(出于教育目的)设置 DSYNC 选项。

这样,文件将在写入输出流的同时写入磁盘。输出文件不会有任何内存缓存。

于 2013-09-04T20:22:51.473 回答