当我参加第四届中间件比赛的时候,我就对这文件读写很懵逼,好多种读写方式,啥区别,效率都啥样,于是翻看了好多的资料,真的不全啊,有的单独说系统,有的单独说java,也不知道谁真谁假。于是根据我查找的资料和实际的源码阅读,我总结这篇文章,希望各位像我一样懵逼过得同学能不太懵逼,希望有理解不到位的地方各位大佬能指点一二。
即将毕业,会陆续开始写一些java方面的文章,希望能为找工作带来些帮助,各位大佬关注一下!
JAVA磁盘IO的前世今生:
很早的时候,各种InputStream OutputStream等等,这些都是基于流的IO,这种IO过时了,流还要分为输入流和输出流。
现在广泛用NIO了,NIO是基于通道Channel的,通道当然就是双向的,一个通道既可以输入又可以输出。而且上面的载体是Buffer,也就是‘块’,而非之前的‘流’。
后面我只说NIO,我个人认为NIO可以代替传统的流了吧
JAVA NIO中的‘载体’——Buffer:
首先载体是块,常用ByteBuffer。也就是与磁盘之间的传输的内容都在这个里面。
看图可知道,这个载体有三类:HeapByteBuffer、DirectByteBuffer、MappedByteBuffer。其中前一个是堆内的byte数组,后两个是堆外的,堆外的意思就是是用jni调用C来malloc的内存,这个内存不在堆内无法gc。
关于Buffer的细节我以后的博文会写一些。
linux文件IO机制:
首先补充一下系统的知识,用户态对应内存的用户空间,也就是我们程序所申请的空间。内核态对应内存的内核空间。系统调用就是从用户态到内核态的一个接口。
听起来感觉没用是吧,以普通方式文件读为例:程序一开始工作在用户空间,然后要读文件,调用read系统调用,转到内核态,DMA进行磁盘io,将数据读到内核空间的缓存(pagecache)中,然后cpu再从pagecache copy到用户空间来,切换为用户态。
上面基础知识了解之后,看一下Linux下的几种不同的磁盘IO:
1.最常见的机制(缓存IO):用户空间<——>内核空间(pagecache)<——>磁盘
2.内存映射机制(mmap):还是有内核区的pagecache,只不过用户空间和内核空间的pagecache有内存映射,减少一次copy
3.直接IO(DIO):不经过内核缓冲区,直接用户空间<——>磁盘
4.异步IO
磁盘IO的零拷贝指的就是2和3
JAVA中磁盘NIO呢?
java不像C++可以那么底层的操作,所以有一定的限制。看一下我总结出来JAVA磁盘NIO的四种方法以及是数据走向
1.filechannel+HeapByteBuffer:堆内<->堆外<->pagecache<->磁盘
2.filechannel+DirectByteBuffer:堆外<->pagecache<->磁盘
3.MappedByteBuffer:堆外<->pagecache<->磁盘 (其中堆外与pagecache有内存映射不需要copy)
4.异步channel
可以看出java没办法直接IO,当然了可以通过jni调用C来实现,这是一种骚操作了。
效率上的差距和注意点呢?
经过首届POLARDB数据库性能大赛的磨练,当然是2、3要好一些了,但是还有些细节要注意,以后说吧。后面我会写一下我们比赛的攻略(第七名),和Buffer使用以及网络NIO等等