老是有人问JAVA IO的性能问题,我按照老的IO最基本的FileInputStream做了几个实验,自己记录下,实验代码都在附件包里。
以下是实验结果和总结
Test07一个字节一个字节的读取消耗时间(毫秒):74969
Test08按照一个byte[1024]==1k读取消耗时间(毫秒):110
Test10按照buffered(缺省size=512)后的fileinputstream的一个字节一个字节读取消耗时间(毫秒):1360
Test11按照buffered(缺省size=512)后的byte[1024]==1k读取消耗时间(毫秒):47
Test12按照buffered(size=1024)后的byte[1024]==1k读取消耗时间(毫秒):110
问题:本来预计Test12比Test11快一倍的,怎么刚好相反了????????
解答:因为原以为buffer的缺省size=512,所以有如下的预计,后来查看源码发现BufferedInputStream 类private static int defaultBufferSize = 8192;
所以
Test10按照buffered(缺省size=8192)后的fileinputstream的一个字节一个字节读取消耗时间(毫秒):1360
Test11按照buffered(缺省size=8192)后的byte[1024]==1k读取消耗时间(毫秒):46
Test12按照buffered(size=1024)后的byte[1024]==1k读取消耗时间(毫秒):109
可以看出Test12和Test08没有区别了,因为这个时候Test12的buffered就失去意义了
而且Test11的buffered虽然是读取块大小的8倍,但是最终的结果并没有提高八倍的速度,因为虽然和系统底层IO的交互
次数提高了8倍(BufferedInputStream 的buffer的作用),但是read的大小还是byte[1024]==1k,也就是说在BufferedInputStream 的buffer
里读的次数和Test08的读取次数一样,但唯一区别是Test11是从BufferedInputStream 的buffer里读的,读的是内存,
而Test08是从文件读的,读的是系统底层IO,所以速度肯定会有提高,但不是提高8倍,
如果想让Test11比Test08快8倍的话,理论上(还有些别的因素的性能消耗)只需要将Test11的读取块设置为byte[8192]==8k即可,而这样也和将Test08的size直接设置为8192就没什么区别了
这是修改后的结果
Test11按照buffered(缺省size=8192)后的byte[8192]==8k读取消耗时间(毫秒):31
Test08按照一个byte[8192]==8k读取消耗时间(毫秒):31
果然效果一样,但都没有110/8的速度。
显然缓冲再往下大下去,瓶颈就不在缓冲上了
由此也可以看出其实如果你用了缺省的BufferedInputStream(没有修改它的size,缺省为8k),和直接用FileInputStream.read(byte[8192])是没有区别的。
你如果用了缺省的BufferedInputStream(没有修改它的size,缺省为8k),再在这个基础上用BufferedInputStream.read(byte[sizeeeee]),当这个sizeeeee比8192
小的话也没有什么意思了,还不如直接用FileInputStream.read(byte[8192]),不加BufferedInputStream包装了。
那么如果用BufferedInputStream包装FileInputStream并且采用了read(byte[sizeeee])时,BufferedInputStream的
buffer<=sizeeee时随意调整buffer大小,效果均和buffer=sizeeee时一样,
因为
//需要读的字节数比BufferedInputStream设置的大,那么每次都会将BufferedInputStream里不能提供的还是需要直接读文件,而此时BufferedInputStream顺道更新自己的buffer为新的.
//读取顺序为file===>BufferedInputStream的buffer===>read(byte[sizeeeee]);
见Test12示例
也就是说如果你用缺省的BufferedInputStream,然后你用了read(byte[sizeeee])并将sizeeee设置为>8192时,再怎么网上加都和sizeeee=8192是没区别的,当然在sizeeee<=8192的
范围内调整sizeeee大小还是有意义的.
关于FileReader的就不再做测试了
注意Test09是用来测试中文用按照字节读取的乱码问题的,如果非要按照字节读中文或是写中文的话需要自己转码
后记:关于buffer在java io中的原理
因为单纯的java的read和write没调一次就需要和系统底层IO交互一次,那么我们就想能在一次交互的过程中尽量读取或是写入最大的内容,那么就减少了交互次数,
从而大大提高了性能。
下面部分内容来自CSDN博客http://blog.csdn.net/redv/archive/2005/03/31/334697.aspx中的一段
影响Java IO性能最主要的原因之一在于大量地使用单字符IO操作,即用InputStream.read()和Reader.read()方法每次读取一个字符。 Java的单字符IO操作继承自C语言。在C语言中,单字符IO操作是一种常见的操作,比如重复地调用getc()读取一个文件。C语言单字符IO操作的效率很高,因为getc()和putc()函数以宏的形式实现,且支持带缓冲的文件访问,因此这两个函数只需要几个时钟周期就可以执行完毕。在Java 中,情况完全不同:对于每一个字符,不仅要有一次或者多次方法调用,而且更重要的是,如果不使用任何类型的缓冲,要获得一个字符就要有一次系统调用。虽然一个依赖read()的Java程序可能在表现、功能上和C程序一样,但两者在性能上不能相提并论。幸而,Java提供了几种简单的办法帮助我们获得更好的IO性能。
缓冲可以用以下两种方式之一实现:使用标准的BufferedReader和BufferedInputStream类,或者使用块读取方法一次读取一大块数据。前者快速简单,能够有效地改进性能,且只需少量地增加代码,出错的机会也较少。后者也即自己编写代码,复杂性略有提高——当然也说不上困难,但它能够获得更好的效果。