Netty的DirectByteBuf的零复制,仅仅是减少了Linux进程内部的内存复制,还没有上升到内核的维度。
Netty的DirectByteBuf的零复制,仅仅是减少了JVM爱冲区到直接的内存复制
只有第五条属于操作系统才层面的零复制
Netty的零拷贝主要体现在以下五个方面:
- Netty提供CompositeByteBuf类,可以将多个ByteBuf合并为个逻辑 上的ByteBuf,避免了各个ByteBuf之间的拷贝。
- 通过wrap操作,我们可以将byte数组、 ByteBuf、 ByteBuffer等包装成个Netty ByteBuf对象,进而避免贝操作。
- ByteBuf支持slice操作,可以将ByteBu分解为多个共享同个存储区域的ByteBuf,避免内存的拷贝。
- Netty使用直接内存,消息在发送过程中少了一次缓冲区的内存拷贝。
- Netty的文件传输调用FileRegion包装的tranaferTo方法,可以直接将文件缓冲区的数据发送到目标Channel,避免通过循环write方式导致的内存拷贝问题。
1、CompositeByteBuf
CompositeByteBuf可以把需要合并的多个ByteBuf组合起来,对外提供统一的readIndex和writerIndex。
import io.netty.buffer.ByteBuf;import io.netty.buffer.ByteBufAllocator;import io.netty.buffer.ByteBufUtil;import io.netty.buffer.CompositeByteBuf;import io.netty.util.internal.StringUtil;/*** @Description:* @Author: zhangjx* @Date: 2021-06-13**/public class TestCompositeByteBuf {public static void main(String[] args) {ByteBuf buffer1 = ByteBufAllocator.DEFAULT.buffer();buffer1.writeBytes(new byte[]{'a','b','c'});ByteBuf buffer2 = ByteBufAllocator.DEFAULT.buffer();buffer2.writeBytes(new byte[]{'d','e','f'});ByteBuf buffer = ByteBufAllocator.DEFAULT.buffer();//会发生两次数据复制buffer.writeBytes(buffer1).writeBytes(buffer2);log(buffer);//需要注意引用计数问题CompositeByteBuf compositeByteBuf = ByteBufAllocator.DEFAULT.compositeBuffer();buffer1.retain();buffer2.retain();//默认不会调整读写指针位置compositeByteBuf.addComponents(buffer1,buffer2);//会调整读写指针位置compositeByteBuf.addComponents(true,buffer1,buffer2);log(compositeByteBuf);buffer1.release();buffer2.release();}private static void log(ByteBuf buffer) {int length = buffer.readableBytes();int rows = length / 16 + (length % 15 == 0 ? 0 : 1) + 4;StringBuilder buf = new StringBuilder(rows * 80 * 2).append("read index:").append(buffer.readerIndex()).append(" write index:").append(buffer.writerIndex()).append(" capacity:").append(buffer.capacity()).append(StringUtil.NEWLINE);ByteBufUtil.appendPrettyHexDump(buf, buffer);System.out.println(buf.toString());}}
2、Unpooled
Unpooled是一个工具类,提供了非池化的Bytebuf创建、组合、复制等操作。其中提供了一系列的wrap包装方法,可以方便快速的包装出CompositeByteBuf实例或者ByteBuf实例,而不用进行内存拷贝。
import io.netty.buffer.ByteBuf;import io.netty.buffer.ByteBufAllocator;import io.netty.buffer.ByteBufUtil;import io.netty.buffer.Unpooled;import io.netty.util.internal.StringUtil;/*** @Description:* @Author: zhangjx* @Date: 2021-06-13**/public class TestUnpooled {public static void main(String[] args) {ByteBuf buffer1 = ByteBufAllocator.DEFAULT.buffer();buffer1.writeBytes(new byte[]{'a','b','c'});ByteBuf buffer2 = ByteBufAllocator.DEFAULT.buffer();buffer2.writeBytes(new byte[]{'d','e','f'});ByteBuf byteBuf = Unpooled.wrappedBuffer(buffer1, buffer2);log(byteBuf);}private static void log(ByteBuf buffer) {int length = buffer.readableBytes();int rows = length / 16 + (length % 15 == 0 ? 0 : 1) + 4;StringBuilder buf = new StringBuilder(rows * 80 * 2).append("read index:").append(buffer.readerIndex()).append(" write index:").append(buffer.writerIndex()).append(" capacity:").append(buffer.capacity()).append(StringUtil.NEWLINE);ByteBufUtil.appendPrettyHexDump(buf, buffer);System.out.println(buf.toString());}}
3、ByteBuf浅层复制
浅层复制可以很大程度避免内存复制。
浅层复制分两种:
- 切片(slice)浅层复制
- 整体(duplicate)浅层复制
3.1、切片浅层复制
ByteBuf的slice方法可以获取一个ByteBuf的切片,一个ByteBuf可以多次切片浅层复制,多次切片后的ByteBuf对象可以共享一个内存区域
切片方法:
public ByteBuf slice()返回ByteBuf实例中可读部分的切片public ByteBuf slice(int var1, int var2)通过设置不同起始位置和长度获取ByteBuf不同区域的切片
切片特点:
- 切片不可以写入,因为读写指针相同
- 切片不会复制源ByteBuf的底层数据,底层数组和源ByteBuf的底层数组是同一个
- 切片不会改变源ByteBuf的引用计数,调用浅层复制实例时,手动调用retain()方法增加引用计数
package com.jx.netty.netty.bytebuf;import io.netty.buffer.ByteBuf;import io.netty.buffer.ByteBufAllocator;import static io.netty.buffer.ByteBufUtil.appendPrettyHexDump;import static io.netty.util.internal.StringUtil.NEWLINE;/*** @Description:* @Author: zhangjx* @Date: 2021-06-09**/public class TestSlice {public static void main(String[] args) {ByteBuf buffer = ByteBufAllocator.DEFAULT.buffer(12);buffer.writeBytes(new byte[]{'a','b','c','d','e','f','g','h','i','j'});log(buffer);//slice()返回了ByteBuf可读部分切片System.out.println("=============================");ByteBuf slice = buffer.slice();log(slice);System.out.println("=============================");ByteBuf buf1 = buffer.slice(0, 5);buf1.retain();ByteBuf buf2 = buffer.slice(5, 5);//手动增加引用计数buf2.retain();log(buf1);log(buf2);System.out.println("=============================");buffer.setByte(0,'x');buffer.setByte(5,'x');log(buf1);log(buf2);System.out.println("=============================");buffer.release();log(buf1);log(buf2);buf1.release();buf2.release();}private static void log(ByteBuf buffer) {int length = buffer.readableBytes();int rows = length / 16 + (length % 15 == 0 ? 0 : 1) + 4;StringBuilder buf = new StringBuilder(rows * 80 * 2).append("read index:").append(buffer.readerIndex()).append(" write index:").append(buffer.writerIndex()).append(" capacity:").append(buffer.capacity()).append(NEWLINE);appendPrettyHexDump(buf, buffer);System.out.println(buf.toString());}}
3.2、整体浅层复制
duplicate()返回的是整个对象的浅层复制:
- duplicate()返回实例读写指针,最大容量和源ByteBuf相同
- duplicate()不会改变源ByteBuf引用计数
- duplicate()不会复制源ByteBuf的底层数据
public static void main(String[] args) {ByteBuf buffer = ByteBufAllocator.DEFAULT.buffer(12);buffer.writeBytes(new byte[]{'a','b','c','d','e','f','g','h','i','j'});log(buffer);//整体浅层复制ByteBuf duplicate = buffer.duplicate();log(duplicate);}
