一、JDKByteBuffer的缺点
1.无法动态扩容:长度是固定,不能动态扩展和收缩,当数据大于ByteBuffer容量时,会发生索引越界异常。
2.API使用复杂:读写的时候需要手工调用flip()和rewind()等方法,使用时需要非常谨慎的使用这些api,否则很容易出现错误。
二、ByteBuf做的增强方面
1.API操作便捷性
2.动态扩容
3.多种ByteBuf实现
4.高效的零拷贝机制
三、netty中对ByteBuf操作
ByteBuf三个重要的属性:capacity容量、readerIndex读取位置、writeindex写入位置。提供了两个指针变量来支持顺序读和写操作,分别为readerIndex和写操作writeIndex。
代码实例:
public class ByteBufDemo {
@Test
public void apiTest() {
// +-------------------+------------------+------------------+
// | discardable bytes | readable bytes | writable bytes |
// | | (CONTENT) | |
// +-------------------+------------------+------------------+
// | | | |
// 0 <= readerIndex <= writerIndex <= capacity
// 1.创建一个非池化的ByteBuf,大小为10个字节
ByteBuf buf = Unpooled.buffer(10);
System.out.println("原始ByteBuf为====================>" + buf.toString());
System.out.println("1.ByteBuf中的内容为===============>" + Arrays.toString(buf.array()) + "\n");
// 2.写入一段内容
byte[] bytes = {1, 2, 3, 4, 5};
buf.writeBytes(bytes);
System.out.println("写入的bytes为====================>" + Arrays.toString(bytes));
System.out.println("写入一段内容后ByteBuf为===========>" + buf.toString());
System.out.println("2.ByteBuf中的内容为===============>" + Arrays.toString(buf.array()) + "\n");
// 3.读取一段内容
byte b1 = buf.readByte();
byte b2 = buf.readByte();
System.out.println("读取的bytes为====================>" + Arrays.toString(new byte[]{b1, b2}));
System.out.println("读取一段内容后ByteBuf为===========>" + buf.toString());
System.out.println("3.ByteBuf中的内容为===============>" + Arrays.toString(buf.array()) + "\n");
// 4.将读取的内容丢弃
buf.discardReadBytes();
System.out.println("将读取的内容丢弃后ByteBuf为========>" + buf.toString());
System.out.println("4.ByteBuf中的内容为===============>" + Arrays.toString(buf.array()) + "\n");
// 5.清空读写指针
buf.clear();
System.out.println("将读写指针清空后ByteBuf为==========>" + buf.toString());
System.out.println("5.ByteBuf中的内容为===============>" + Arrays.toString(buf.array()) + "\n");
// 6.再次写入一段内容,比第一段内容少
byte[] bytes2 = {1, 2, 3};
buf.writeBytes(bytes2);
System.out.println("写入的bytes为====================>" + Arrays.toString(bytes2));
System.out.println("写入一段内容后ByteBuf为===========>" + buf.toString());
System.out.println("6.ByteBuf中的内容为===============>" + Arrays.toString(buf.array()) + "\n");
// 7.将ByteBuf清零
buf.setZero(0, buf.capacity());
System.out.println("将内容清零后ByteBuf为==============>" + buf.toString());
System.out.println("7.ByteBuf中的内容为================>" + Arrays.toString(buf.array()) + "\n");
// 8.再次写入一段超过容量的内容
byte[] bytes3 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
buf.writeBytes(bytes3);
System.out.println("写入的bytes为====================>" + Arrays.toString(bytes3));
System.out.println("写入一段内容后ByteBuf为===========>" + buf.toString());
System.out.println("8.ByteBuf中的内容为===============>" + Arrays.toString(buf.array()) + "\n");
}
四、动态扩容
capacity默认值:256字节、最大值:Integer.MAX_VALUE(2GB);write*方法调用时,通过AbstractByteBuf.ensureWritable()进行检查。容量计算方法:AbstractByteBufAllocator.calculateNewCapacity(新capacity的最小要求,capacity最大值)根据capacity的最小值要求,对应有两套计算方法:
1.没超过4兆:从64字节开始,每次增加一倍,直至计算出来的newCapacity满足新容量最小要求。实例:当前大小256,已写250,继续写10字节数据,需要的容量最小要求是261,则新容量是64*2*2*2 = 512。
2.超过4兆:新容量 = 新容量最小要求/4兆 * 4兆 + 4兆。实例:当前大小3兆,已写3兆,继续写2兆数据,需要的容量最小要求是5兆,则新容量是9兆(不能超过最大值)。
五、丰富的byteBuf实现
netty中根据3个纬度,可以划分出8中byteBuf的实现:
堆外/堆内 | 是否池化 | 访问方式 | 具体实现类 | 备注 |
heap堆内 | unpool | safe | UnpooledHeapByteBuf | 数组实现 |
unsafe | UnpooledUnsafeHeapByteBuf | unsafe类直线操作内存 | ||
pool | safe | PooledHeapByteBuf | ||
unsafe | PooledUnsafeHeapByteBuf | |||
direct堆外 | unpool | safe | UnpooledDirectByteBuf | NIO DirectByteBuffer |
unsafe | UnpooledUnsafeDirectByteBuf | |||
pool | safe | PooledDirectByteBuf | ||
unsafe | PooledUnsafeDirectByteBuf |
在使用中,都是通过ByteBufAllocator分配器进行申请,同时分配器具备有内存管理的功能。
六、零拷贝机制
netty的零拷贝机制,是一种应用层的实现。和底层JVM、操作系统内存机制并无过多关联。
- CompositeByteBuf,将多个ByteBuf合并为一个逻辑上的ByteBuf,避免了各个ByteBuf之间的拷贝。
- wrapedBuffer()方法,将byte[]数组包装成ByteBuf对象
- slice()方法。将一个ByteBuf对象切分成多个ByteBuf对象。