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。

  1. import io.netty.buffer.ByteBuf;
  2. import io.netty.buffer.ByteBufAllocator;
  3. import io.netty.buffer.ByteBufUtil;
  4. import io.netty.buffer.CompositeByteBuf;
  5. import io.netty.util.internal.StringUtil;
  6. /**
  7. * @Description:
  8. * @Author: zhangjx
  9. * @Date: 2021-06-13
  10. **/
  11. public class TestCompositeByteBuf {
  12. public static void main(String[] args) {
  13. ByteBuf buffer1 = ByteBufAllocator.DEFAULT.buffer();
  14. buffer1.writeBytes(new byte[]{'a','b','c'});
  15. ByteBuf buffer2 = ByteBufAllocator.DEFAULT.buffer();
  16. buffer2.writeBytes(new byte[]{'d','e','f'});
  17. ByteBuf buffer = ByteBufAllocator.DEFAULT.buffer();
  18. //会发生两次数据复制
  19. buffer.writeBytes(buffer1).writeBytes(buffer2);
  20. log(buffer);
  21. //需要注意引用计数问题
  22. CompositeByteBuf compositeByteBuf = ByteBufAllocator.DEFAULT.compositeBuffer();
  23. buffer1.retain();
  24. buffer2.retain();
  25. //默认不会调整读写指针位置
  26. compositeByteBuf.addComponents(buffer1,buffer2);
  27. //会调整读写指针位置
  28. compositeByteBuf.addComponents(true,buffer1,buffer2);
  29. log(compositeByteBuf);
  30. buffer1.release();
  31. buffer2.release();
  32. }
  33. private static void log(ByteBuf buffer) {
  34. int length = buffer.readableBytes();
  35. int rows = length / 16 + (length % 15 == 0 ? 0 : 1) + 4;
  36. StringBuilder buf = new StringBuilder(rows * 80 * 2)
  37. .append("read index:").append(buffer.readerIndex())
  38. .append(" write index:").append(buffer.writerIndex())
  39. .append(" capacity:").append(buffer.capacity())
  40. .append(StringUtil.NEWLINE);
  41. ByteBufUtil.appendPrettyHexDump(buf, buffer);
  42. System.out.println(buf.toString());
  43. }
  44. }


2、Unpooled

Unpooled是一个工具类,提供了非池化的Bytebuf创建、组合、复制等操作。其中提供了一系列的wrap包装方法,可以方便快速的包装出CompositeByteBuf实例或者ByteBuf实例,而不用进行内存拷贝。

  1. import io.netty.buffer.ByteBuf;
  2. import io.netty.buffer.ByteBufAllocator;
  3. import io.netty.buffer.ByteBufUtil;
  4. import io.netty.buffer.Unpooled;
  5. import io.netty.util.internal.StringUtil;
  6. /**
  7. * @Description:
  8. * @Author: zhangjx
  9. * @Date: 2021-06-13
  10. **/
  11. public class TestUnpooled {
  12. public static void main(String[] args) {
  13. ByteBuf buffer1 = ByteBufAllocator.DEFAULT.buffer();
  14. buffer1.writeBytes(new byte[]{'a','b','c'});
  15. ByteBuf buffer2 = ByteBufAllocator.DEFAULT.buffer();
  16. buffer2.writeBytes(new byte[]{'d','e','f'});
  17. ByteBuf byteBuf = Unpooled.wrappedBuffer(buffer1, buffer2);
  18. log(byteBuf);
  19. }
  20. private static void log(ByteBuf buffer) {
  21. int length = buffer.readableBytes();
  22. int rows = length / 16 + (length % 15 == 0 ? 0 : 1) + 4;
  23. StringBuilder buf = new StringBuilder(rows * 80 * 2)
  24. .append("read index:").append(buffer.readerIndex())
  25. .append(" write index:").append(buffer.writerIndex())
  26. .append(" capacity:").append(buffer.capacity())
  27. .append(StringUtil.NEWLINE);
  28. ByteBufUtil.appendPrettyHexDump(buf, buffer);
  29. System.out.println(buf.toString());
  30. }
  31. }

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()方法增加引用计数
  1. package com.jx.netty.netty.bytebuf;
  2. import io.netty.buffer.ByteBuf;
  3. import io.netty.buffer.ByteBufAllocator;
  4. import static io.netty.buffer.ByteBufUtil.appendPrettyHexDump;
  5. import static io.netty.util.internal.StringUtil.NEWLINE;
  6. /**
  7. * @Description:
  8. * @Author: zhangjx
  9. * @Date: 2021-06-09
  10. **/
  11. public class TestSlice {
  12. public static void main(String[] args) {
  13. ByteBuf buffer = ByteBufAllocator.DEFAULT.buffer(12);
  14. buffer.writeBytes(new byte[]{'a','b','c','d','e','f','g','h','i','j'});
  15. log(buffer);
  16. //slice()返回了ByteBuf可读部分切片
  17. System.out.println("=============================");
  18. ByteBuf slice = buffer.slice();
  19. log(slice);
  20. System.out.println("=============================");
  21. ByteBuf buf1 = buffer.slice(0, 5);
  22. buf1.retain();
  23. ByteBuf buf2 = buffer.slice(5, 5);
  24. //手动增加引用计数
  25. buf2.retain();
  26. log(buf1);
  27. log(buf2);
  28. System.out.println("=============================");
  29. buffer.setByte(0,'x');
  30. buffer.setByte(5,'x');
  31. log(buf1);
  32. log(buf2);
  33. System.out.println("=============================");
  34. buffer.release();
  35. log(buf1);
  36. log(buf2);
  37. buf1.release();
  38. buf2.release();
  39. }
  40. private static void log(ByteBuf buffer) {
  41. int length = buffer.readableBytes();
  42. int rows = length / 16 + (length % 15 == 0 ? 0 : 1) + 4;
  43. StringBuilder buf = new StringBuilder(rows * 80 * 2)
  44. .append("read index:").append(buffer.readerIndex())
  45. .append(" write index:").append(buffer.writerIndex())
  46. .append(" capacity:").append(buffer.capacity())
  47. .append(NEWLINE);
  48. appendPrettyHexDump(buf, buffer);
  49. System.out.println(buf.toString());
  50. }
  51. }

3.2、整体浅层复制

duplicate()返回的是整个对象的浅层复制:

  • duplicate()返回实例读写指针,最大容量和源ByteBuf相同
  • duplicate()不会改变源ByteBuf引用计数
  • duplicate()不会复制源ByteBuf的底层数据
  1. public static void main(String[] args) {
  2. ByteBuf buffer = ByteBufAllocator.DEFAULT.buffer(12);
  3. buffer.writeBytes(new byte[]{'a','b','c','d','e','f','g','h','i','j'});
  4. log(buffer);
  5. //整体浅层复制
  6. ByteBuf duplicate = buffer.duplicate();
  7. log(duplicate);
  8. }