背景
前段时间在同事提交的代码中看到这样一段代码,非常简单,就是通过BufferedReader缓冲区模式读磁盘文件的代码,缓冲区的大小设置为10000。
原理简介
在我的印象中,很少有自己去设置缓冲区大小的,都是使用默认的缓冲区大小。而通过JDK的IO流源码可以发现,缓冲区的默认大小是8192个字符,一个字符是2个字节,所以JAVA IO流的默认缓存区大小是16384个字节即2的14次方。注意:设置缓冲区的大小不要随便设置,要么就设置成8192的整数倍,要么就用默认值。
实际上,从磁盘读取的数据会保存到Page Cache中,Page Cache的中文译名叫页高速缓存,是Linux内核使用的磁盘高速缓存,是一个纯内存的工作组件,它的作用就是为比较慢的磁盘进行加速。如果要访问的文件块正好位于Page Cache内,那么不会发生磁盘IO;否则会发生缺页中断,并用从磁盘读取到的块内容来填充它 。MMAP内存映射文件的零拷贝技术就是访问的数据不需要在page cache和用户缓冲区之间进行拷贝。
Page Cache以页为单位,Linux页大小一般是4KB,而文件系统以块为单位进行管理,一般一个块的大小默认是4KB。通常情况下,磁盘的扇区为512字节,因此一个磁盘块包含8个扇区。
极端情况下讨论,便于理解:假如文件大小是16384 * 2个字节。一次磁盘IO就是读取一个磁盘块的内容,JAVA IO流默认的缓存区大小是16384个字节,相当于读取4次磁盘块的内容刚好填充满,按照JDK默认值读完整个文件需要8次磁盘IO。如果将缓冲区的大小设置为10000即20000个字节要读取5次磁盘块的内容,而第5次的内容会多出,即前5次磁盘IO读取了20000个字节,还剩下12768个字节,一次磁盘IO是4096个字节,这里需要4次磁盘IO,因此当设置为10000时,总共需要9次磁盘IO。
性能测试
还是通过JMH基准测试工具进行测试,预热一轮,迭代一轮,读取的文件大小一样,都是123MB,测试代码如下:
测试结果
缓冲区设置为10000的耗时330.853毫秒,设置为8192的耗时261.777毫秒,性能差不多提升了26%。