读取10G的文本文件,我们可能会直接认为,这太难了,这涉及到内存的容量,硬盘的读取速度以及虚拟内存、页失败载入等概念,其实对JAVA,读取10G的文件轻而易举,无论bio(java.io)还是nio,都能轻松完成任务。

1. FileInputStream VS BufferedInputStream

不用做任何优化,也不用调用任何第三方库,FileInputStream即可独立完成工作,并且仅仅消耗内存20M。

跟FileInputStream相比,BufferedInputStream并没有体现出任何性能优势,以下是它们的成绩对比:

实现方式

文件大小

执行时间

消耗内存

FileInputStream

9.5G

22.826411809

20M

BufferedInputStream

9.5G

22.60415508

20M

FileInputStream的实现代码如下:

private static long readByFis(File file) throws FileNotFoundException, IOException {
    InputStream is = new FileInputStream(file);
    byte[] buff = new byte[4096];
    long counts = 0;
    int offset = 0;
    while((offset = is.read(buff)) != -1) {
        counts = counts + offset;
    }
    is.close();
    return counts;
}

2. BufferedReader

对于字符处理,BufferedReader通常是我的第一选择,但从我多次测试的结果来看,字符的处理效率远远低于二进制的处理效率(内存消耗与处理时间都有较大幅度的增长),以下是成绩对比及代码实现:

实现方式

文件大小

执行时间

消耗内存

BufferedReader

9.5G

28.607785218S

40M

BufferedInputStream

9.5G

22.60415508

20M

private static long readByBos(File file) throws FileNotFoundException, IOException {
    long counts = 0;
    int offset = 0;
    BufferedInputStream bos = new BufferedInputStream(new FileInputStream(file));
    byte[] buff = new byte[4096];
    while((offset= bos.read(buff)) != -1) {
        counts = counts + offset;
    }
    bos.close();
    return counts;
}

说一句体外话,对于文件编辑器,经常出现卡顿、卡死打不开文件的原因,大部分的原因都不在于磁盘IO与内存,而在于文本处理与排版。

3. 文件通道与直接内存

在文件读取操作中,直接内存依旧没有显示出性能优势,以下是他们的成绩对比:

实现方式

文件大小

执行时间

缓冲区大小

非直接内存

9.5G

27.852194903

2M

非直接内存

9.5G

27.083122525

4M

直接内存

9.5G

27.374642641

2M

直接内存

9.5G

27.332383289

4M

现在我很怀疑,到底是JAVA对传统的IO进行了较大幅度的优化,还是nio的优势根本就不在于性能,因为读取速度上,nio表现依旧很平凡,以下是实现代码:

private static long readByChannel() throws FileNotFoundException, IOException {
    long counts = 0;
    File file = new File("bigFile.txt");
    FileInputStream fis = new FileInputStream(file);
    FileChannel fc = fis.getChannel();
    ByteBuffer bbuf = ByteBuffer.allocate(2048);
    int offset = 0;
    while((offset = fc.read(bbuf)) != -1) {
        counts = counts + offset;
        bbuf.clear();
    }
    fc.close();
    fis.close();
    return counts;
}

4. 内存映射

这里的操作技巧最强,但总体来说,映射的内容越大越好,对于反复处理的内容,性能优势较为明显,这里打算单独来进行评测。

结论

读取10G的文件,JAVA的传统API即可轻松完成,并且性能卓越,至于NIO,强化的可能不仅仅是性能,而是新的编程方式与阻塞处理机制。