百万并发「零拷贝」技术系列之 Java 实现
在上一篇推文百万并发「零拷贝」技术系列之Linux实现 讲解了零拷贝思想在Linux系统中主要有mmap、sendfile、splice、tee等实现,但在Java中目前主要实现了mmap和sendfile。
Java I/O的发展史
在百万并发「零拷贝」技术系列之初探门径 推文中,我们了解到为了降低内核接口调用的复杂度和提高编码效率,高级语言一般都为程序开发者提供了封装的类库,如C语言的标准库、Java的JDK等。
在JDK1.3之前Java的I/O一直比较传统,是采用Stream阻塞模式。在JDK1.4 的发布版中正式引入NIO,加入了缓冲区Buffer和通道Channel的概念,提供了非阻塞的方式。然而JDK1.4主要是为Socket通讯进行的优化,随后在JDK1.7版本中的NIO2不仅增强了文件系统的处理能力,还做到了真正的异步I/O—AIO。
mmap的实现 - MappedByteBuffer
JDK NIO提供的MappedByteBuffer底层就是调用mmap来实现的,FileChannel.map用来建立内存映射关系:把用户空间和内存空间的虚拟内存地址映射到同一块物理内存。mmap对大文件比较合适,对小文件则容易造成内存碎片,反而不是最佳使用场景。
编码示例如下
sendfile的实现 - transferTo
NIO提供的FileChannel.transferTo方法可以直接将一个channel传递给另一个channel,结合上一篇推文看,channel像极了内核缓冲区。
编码示例如下
传统I/O vs mmap vs sendfile
通过实战来对比下传统I/O、mmap、sendfile的性能及在用户空间和内核空间中消耗的CPU时间,代码如下
首先进行代码编译java javac JioChannel.java,它的执行方法是JioChannel <source> <destination> <mode>,其中mode值1为传统方式I/O,2为mmap方式I/O,3为sendfile方式I/O。
执行和输出如下(a.zip为130M的压缩文件)
user+sys之和是该执行进程的耗费CPU的总时间,可见mmap和sendfile方式效率高于传统方式,而且用户空间user耗费CPU的时间占比总耗费时间也有所降低。
Linux的time命令
time是linux shell内置的命令,它用于统计/测量系统的资源使用情况,如CPU、内存、I/O等,用法如下
内存、I/O等资源可参看time手册,不展开叙述。测量CPU的主要角度是其耗费的时间:实际总耗费时间、用户空间和内核空间各自耗费的时间。
real:实际总耗费时间,从会话开始到结束,包括其他进程的使用时间和本进程阻塞的时间;
user:该执行进程在用户空间耗费的CPU时间;
sys:该执行进程在内核空间耗费的CPU时间(CPU耗费在系统调用(system calls)执行上);
user + sys:该执行进程实际耗费的CPU总时间,real时间远大于user+sys,因为它不仅包含其他进程消耗的时间,还有文件寻址等时间消耗。
写在最后
虽然JDK没有实现所有的Linux零拷贝模式,但如果能把mmap和sendfile发挥到极致在性能上也能具有非常可观的提升,比如kafka、netty都是以零拷贝而业界瞩目。下一篇推文将简单介绍下kafka、netty的零拷贝思想及实现,这也是面试经常遇到的问题,敬请关注。
最新、更多漫画请关注微信公众号:码农神说。
码农神说:图解码农技术,大话码农故事,漫画感悟码农人生,助力码农翻身!
版权声明: 本文为 InfoQ 作者【码农神说】的原创文章。
原文链接:【http://xie.infoq.cn/article/6c95a0ae1e1356f473a522ce2】。文章转载请联系作者。
评论