操作系统的ZeroCopy 简单操作
byte[] buf=new byte[1024];
InputStream inputStream=new FileInputStream("in.txt");
OutputStream outputStream = new FileOutputStream("out.txt");
int read;
while ((read=inputStream.read(buf))!=-1){
outputStream.write(buf,0,read);
}
inputStream.close();
outputStream.close();
这一段代码是一个经典的将in.txt文件复制到out.txt的一段代码,看起来非常简单,然而其中包含着非常多的操作,下面的图表明了这段代码发生的事情
文章图片
其中至少包含了4次数据的复制,并且包含了多次用户空间和内核空间的切换.其中**DMA(Direct Memory Access)**叫做直接内存访问
- read将数据从通过DMA将硬件数据复制到old fd相关的内核缓冲区中
- 内核数据缓冲区的数据复制到用户空间
- 用户空间复制到new fd对应的内核缓冲区
- DMA将内核空间缓冲区数据复制到协议引擎,第四次复制
- 协议引擎进行数据写入
write的时候需要复制到fd相关的内核缓冲区中,因为是另外一个fd
使用内存映射优化的操作 对于数据的复制,用户空间的数据其实是不需要的,因此可以针对这种问题进行优化,即不进行内核态到用户态的数据复制,即使用内存映射.
内存映射可以将内核空间中的内存块关联到用户空间,使得用户空间看起来在操作内核空间一样,底层是使用mmap函数
使用这个函数可以去除内核空间复制到用户空间的操作,但仍然需要从old fd复制到new fd空间
因此,数据的复制操作可以减少为3次.但用户态的切换次数仍然不变
在Java中使用MappedByteBuffer实现这个操作,实际上底层操作的是DirectByteBuffer
FileChannel channel=...
MappedByteBuffer map=channel.map(FileChannel.MapMode.READ_WRITE,0,5);
map.set(1,'b')
FileChannel可以来自于File和IO
使用sendFile的操作 最少用户态切换的sendFile操作
除上述操作之外,还可以使用内核2.1之后提供的sendFile操作,进行直接复制的操作来避免用户态的切换
如下图
文章图片
- sendFile将硬件数据复制到old fd关联的内核缓冲区中
- 将数据复制到new fd关联的内核缓冲区
- DMA将内核缓冲区复制到协议引擎
- 协议引擎进行数据写入
完全消除不必要复制的sendFile
这个方式还可以进行优化,其中数据复制到新文件描述符的操作不是必要的,可以通过内存地址的方式寻址到第一次复制的数据.即关联old fd和new fd即可
文章图片
- sendFile将硬件数据复制到内核缓冲区中
- 将old fd和new fd进行关联
- 将old fd中的数据写入到硬件
上述使用sendFile的方式仅适用于不需要操作数据的简单复制,如果需要操作数据,最优的方式是mmap的方式
netty的ZeroCopy 与操作系统的ZeroCopy,netty由于本身不仅仅是数据的复制,大部分情况下,netty都需要进行数据的操作,因此netty更加关注的是避免复制.
netty提供了许多种避免复制的方式,但本质上都是一样的,即通过视图的方式进行,如果熟悉数据库的话马上就能够理解意思了.视图不是真实存在的数据,而是对真实数据的一种访问方式.另外对于不需要操作数据的情况,jdk本身就已经提供了transferTo和transferFrom函数,netty进行了一点封装以便ByteBuf使用
多个buffer的合并视图 CompositeByteBuf
JDK的ByteBuffer未找到类似的操作
netty使用compositeByteBuf.addComponents相关的函数进行操作
原生类型的视图 JDK通过ByteBuffer.wrap相关函数进行操作
netty使用Unpooled.wrappedBuffer相关的函数进行操作
单个buffer的部分视图 JDK使用ByteBuffer.slice()函数进行操作
netty使用ByteBuf.slice相关的函数进行操作
直接复制 【netty|ZeroCopy】netty使用FileRegion,实际上底层还是使用的JDK的transfer
推荐阅读
- Java|Java基础——数组
- 人工智能|干货!人体姿态估计与运动预测
- java简介|Java是什么(Java能用来干什么?)
- Java|规范的打印日志
- Linux|109 个实用 shell 脚本
- 程序员|【高级Java架构师系统学习】毕业一年萌新的Java大厂面经,最新整理
- Spring注解驱动第十讲--@Autowired使用
- SqlServer|sql server的UPDLOCK、HOLDLOCK试验
- jvm|【JVM】JVM08(java内存模型解析[JMM])
- 技术|为参加2021年蓝桥杯Java软件开发大学B组细心整理常见基础知识、搜索和常用算法解析例题(持续更新...)