实验背景
最近在考虑一个问题:“如果快速地向文件中写入数据”,java提供了多种文件写入的方式,效率上各有异同,基本上可以分为如下三大类:字节流输出、字符流输出、内存文件映射输出。前两种又可以分为带buffer及不带buffer。
实验目标
通过实验找出单线程场景下文件写入效率最高的方式,并量化各种文件写入方式在效率上的差距。
实验设计
为减少其他环节的影响,示例代码使用循环输出同一字符串多次的方式测试整个输出环节的耗时,通过耗时、服务器资源使用情况(cpu、内存使用)等指标评判各种文件写入方式的效率好坏。
实验环境
OS: windows 7 64bit
cpu: 4core, 主频:2.4GHZ
mem:6G
jdk version:Java HotSpot(TM) 64-Bit ,1.8.0_101
实验步骤
总共进行8次输出实验,依次输出的文件大小为:10M、50M、100M、200M、500M、800M、1G、2G;
每次输出后记录各种方式的耗时情况,并填表。
实验结果
表1、java各种文件写入方法效率对比实验时延数据表
表1、java各种文件写入方法效率对比实验时延结果图(含大文件)
表2、java各种文件写入方法效率对比实验时延结果图(不含大文件)
图2、文件大小为2048m时,各个写入方式执行任务时cpu使用情况:
图3、文件大小为2048m时,各个写入方式执行任务时jvm内存使用情况
图4、文件大小为2048m时,各个写入方式执行任务时物理内存使用情况:
上图中红圈中为‘FileChannel’方式下的物理内存使用情况。
实验结论
1、基本上,五种写入方式的时延从小到大排序为:FileChannel<BufferedOutputStream<FileOutputStream<BufferedWriter<FileWriter;从表1、图1;可以看出;且该规律在小文件写入的情景下,更为明显,图2可以看出;
2、在同样文件大小写入的场景中,通常意义上带buffer的字节流输入/字符流输入比不带buffer的对应流效率要高;
3、各个写入方式的jvm cpu和内存使用情况大致相当,从图2和图3可以看出;
4、文件达到一定大小后( fileSize >=1.5G ),FileChannel的时延变得很大且不稳定,从图1最右边可以看出;同时,物理内存的使用量基本和写入文件大小相当,从图4可以看出;原因在于FileChannel使用MappedByteBuffer写入,这个buffer是direct buffer,直接操作物理内存写入,故而造成物理内存消耗严重。
5、小文件写入的场景下(1M左右),FileChannel有些大材小用了,效率上反而没有字节流效率高。
综上,我们可以得到几条有价值的使用经验:
1、小文件(几M的文件)写入时,使用常规的io输入就行,最优选择是BufferedInportStream,没有必要使用nio的FileChannel;
2、大文件(fileSize > 1G,这是个经验值,需要根据具体环境具体分析)写入时,使用FileChannel需要小心物理内存的瓶颈带来的写入效率低下,可以考虑使用分段写入的方式(TODO:后续实验给出);
3、其他场景下,如果效率优先的考虑,则优先选择FileChannel写入文件。
demo
参考git仓库:https://github.com/daoqidelv/io-demo