这时调用 slice 进行切片,无参 slice 是从原始 ByteBuf 的 read index 到 write index 之间的内容进行切片,切片后的 max capacity 被固定为这个区间的大小,因此不能追加 write
1. ByteBuf slice = origin.slice(); 2. System.out.println(ByteBufUtil.prettyHexDump(slice)); 3. // slice.writeByte(5); 如果执行,会报 IndexOutOfBoundsException 异常
1. +-------------------------------------------------+ 2. | 0 1 2 3 4 5 6 7 8 9 a b c d e f | 3. +--------+-------------------------------------------------+----------------+ 4. |00000000| 02 03 04 |... | 5. +--------+-------------------------------------------------+----------------+
如果原始 ByteBuf 再次读操作(又读了一个字节)
1. origin.readByte(); 2. System.out.println(ByteBufUtil.prettyHexDump(origin));
这时的 slice 不受影响,因为它有独立的读写指针
1. +-------------------------------------------------+ 2. | 0 1 2 3 4 5 6 7 8 9 a b c d e f | 3. +--------+-------------------------------------------------+----------------+ 4. |00000000| 02 03 04 |... | 5. +--------+-------------------------------------------------+----------------+
如果 slice 的内容发生了更改
1. slice.setByte(2, 5); 2. System.out.println(ByteBufUtil.prettyHexDump(slice));
1. +-------------------------------------------------+ 2. | 0 1 2 3 4 5 6 7 8 9 a b c d e f | 3. +--------+-------------------------------------------------+----------------+ 4. |00000000| 02 03 05 |... | 5. +--------+-------------------------------------------------+----------------+
这时,原始 ByteBuf 也会受影响,因为底层都是同一块内存
System.out.println(ByteBufUtil.prettyHexDump(origin));
1. +-------------------------------------------------+ 2. | 0 1 2 3 4 5 6 7 8 9 a b c d e f | 3. +--------+-------------------------------------------------+----------------+ 4. |00000000| 03 05 |.. | 5. +--------+-------------------------------------------------+----------------+
其他拷贝方式 duplicate& copy&CompositeByteBuf
duplicate
【零拷贝】的体现之一,就好比截取了原始 ByteBuf 所有内容,并且没有 max capacity 的限制,也是与原始 ByteBuf 使用同一块底层内存,只是读写指针是独立的
copy
会将底层内存数据进行深拷贝,因此无论读写,都与原始 ByteBuf 无关
CompositeByteBuf
【零拷贝】的体现之一,可以将多个 ByteBuf 合并为一个逻辑上的 ByteBuf,避免拷贝
有两个 ByteBuf 如下
1. ByteBuf buf1 = ByteBufAllocator.DEFAULT.buffer(5); 2. buf1.writeBytes(new byte[]{1, 2, 3, 4, 5}); 3. ByteBuf buf2 = ByteBufAllocator.DEFAULT.buffer(5); 4. buf2.writeBytes(new byte[]{6, 7, 8, 9, 10}); 5. System.out.println(ByteBufUtil.prettyHexDump(buf1)); 6. System.out.println(ByteBufUtil.prettyHexDump(buf2));
1. +-------------------------------------------------+ 2. | 0 1 2 3 4 5 6 7 8 9 a b c d e f | 3. +--------+-------------------------------------------------+----------------+ 4. |00000000| 01 02 03 04 05 |..... | 5. +--------+-------------------------------------------------+----------------+ 6. +-------------------------------------------------+ 7. | 0 1 2 3 4 5 6 7 8 9 a b c d e f | 8. +--------+-------------------------------------------------+----------------+ 9. |00000000| 06 07 08 09 0a |..... | 10. +--------+-------------------------------------------------+----------------+
现在需要一个新的 ByteBuf,内容来自于刚才的 buf1 和 buf2,如何实现?
方法1:
1. ByteBuf buf3 = ByteBufAllocator.DEFAULT 2. .buffer(buf1.readableBytes()+buf2.readableBytes()); 3. buf3.writeBytes(buf1); 4. buf3.writeBytes(buf2); 5. System.out.println(ByteBufUtil.prettyHexDump(buf3));
结果
1. +-------------------------------------------------+ 2. | 0 1 2 3 4 5 6 7 8 9 a b c d e f | 3. +--------+-------------------------------------------------+----------------+ 4. |00000000| 01 02 03 04 05 06 07 08 09 0a |.......... | 5. +--------+-------------------------------------------------+----------------+
这种方法好不好?
回答:是不太好,因为进行了数据的内存复制操作
方法2:
1. CompositeByteBuf buf3 = ByteBufAllocator.DEFAULT.compositeBuffer(); 2. // true 表示增加新的 ByteBuf 自动递增 write index, 否则 write index 会始终为 0 3. buf3.addComponents(true, buf1, buf2);
结果是一样的
1. +-------------------------------------------------+ 2. | 0 1 2 3 4 5 6 7 8 9 a b c d e f | 3. +--------+-------------------------------------------------+----------------+ 4. |00000000| 01 02 03 04 05 06 07 08 09 0a |.......... | 5. +--------+-------------------------------------------------+----------------+
CompositeByteBuf 是一个组合的 ByteBuf,它内部维护了一个 Component 数组,每个 Component 管理一个 ByteBuf,记录了这个 ByteBuf 相对于整体偏移量等信息,代表着整体中某一段的数据。
- 优点,对外是一个虚拟视图,组合这些 ByteBuf 不会产生内存复制
- 缺点,复杂了很多,多次操作会带来性能的损耗
Unpooled
Unpooled 是一个工具类,类如其名,提供了非池化的 ByteBuf 创建、组合、复制等操作
这里仅介绍其跟【零拷贝】相关的 wrappedBuffer 方法,可以用来包装 ByteBuf
1. ByteBuf buf1 = ByteBufAllocator.DEFAULT.buffer(5); 2. buf1.writeBytes(new byte[]{1, 2, 3, 4, 5}); 3. ByteBuf buf2 = ByteBufAllocator.DEFAULT.buffer(5); 4. buf2.writeBytes(new byte[]{6, 7, 8, 9, 10}); 5. 6. // 当包装 ByteBuf 个数超过一个时, 底层使用了 CompositeByteBuf 7. ByteBuf buf3 = Unpooled.wrappedBuffer(buf1, buf2); 8. System.out.println(ByteBufUtil.prettyHexDump(buf3));
1. +-------------------------------------------------+ 2. | 0 1 2 3 4 5 6 7 8 9 a b c d e f | 3. +--------+-------------------------------------------------+----------------+ 4. |00000000| 01 02 03 04 05 06 07 08 09 0a |.......... | 5. +--------+-------------------------------------------------+----------------+
也可以用来包装普通字节数组,底层也不会有拷贝操作
1. ByteBuf buf4 = Unpooled.wrappedBuffer(new byte[]{1, 2, 3}, new byte[]{4, 5, 6}); 2. System.out.println(buf4.getClass()); 3. System.out.println(ByteBufUtil.prettyHexDump(buf4));
1. class io.netty.buffer.CompositeByteBuf 2. +-------------------------------------------------+ 3. | 0 1 2 3 4 5 6 7 8 9 a b c d e f | 4. +--------+-------------------------------------------------+----------------+ 5. |00000000| 01 02 03 04 05 06 |...... | 6. +--------+-------------------------------------------------+----------------+