在netty中 ByteBuf
是一个高效的缓冲区类,,,它替代了java的nio中的ByteBuffer
ByteBuf 和 ByteBuffer比较
ByteBuffer
:
- 读和写共用一个指针,,读写切换的时候,,需要修改指针的位置,
- 固定的大小,不会自动扩容
而ByteBuff
: 是支持动态扩容,,,,他有两个指针,,一个是读指针,一个是写指针,,就不用来回切换读写模式。。。 已经读过的数据叫做废弃部分
ByteBuf
分类内存,默认是分配的堆外内存
,也就是直接内存
,,创建的代价很大,读写的效率很高,因为是零拷贝
而堆内存
: 创建的效率高,读写效率低
由于ByteBuf一般都使用堆外内存
,创建的代价很大,,netty提供了一个池化机制
ByteBuf优势总结:
- 池化思想
- 读写指针分离,不需要切换模式
- 自动扩容
- 支持链式调用
- 零拷贝: slice duplicate , compositeByteBuf,,
ByteBuf内存回收
- UnpooledHeapByteBuf ===> 用的堆内存, jvm的gc回收
- UnpooledDirectByteBuff ===》 用的堆外内存,,可以等gc,也可以显示释放
- PooledByteBuf ===〉 把ByteBuf占用的内存,归还到内存池中,,后面复用,,池化ByteBuf必须手动释放,否则会内存泄漏
netty使用引用计数(ReferenceCounted)
来管理ByteBuf,如果有人使用,就调用retain()方法
计数会加1,调用release()方法
计数会减1,,当这个计数是0的时候,表示这个ByteBuf是可以回收的
ByteBuf常用方法
- writeByte()
- readByte()
- capacity() : 缓冲区容量
- readableBytes() : 可读字节数
- writableBytes()
- markReaderIndex() : 记录指针位置
- resetReaderIndex() : 重置指针位置
- copy() : 复制: 复制不会影响原来的ByteBuf,而是新创一个新的内存,,是内存数据的深拷贝
零拷贝:
- slice() : 切片: 不会复制数据,而是创建一个ByteBuf共享这个内存
切片出来的新的ByteBuf不允许往后新增数据,,因为共用的一个内存,指针会出问题
java"> public static void main(String[] args) {ByteBuf buf = ByteBufAllocator.DEFAULT.buffer(10);buf.writeBytes(new byte[]{1,2,3,4,5,6,7,8,9,10});// 切片过程中,,没有发生数据复制// 添加新的内容,,会影响别的切片,,所以不允许再往后面添加新的数据,,切面会对最大容量有所限制// 如果对原有的buf进行release,,切片的buf和原始的是同一个内存,,ByteBuf f1 = buf.slice(0, 5);// 让他的引用计数+1,, 释放不掉f1.retain();ByteBuf f2 = buf.slice(5, 5);System.out.println("f1 = " + f1);System.out.println("f2 = " + f2);f1.setByte(0,99);byte aByte = buf.getByte(0);System.out.println(aByte);buf.release();byte aByte1 = f1.getByte(0);System.out.println("aByte1 = " + aByte1);byte aByte2 = f2.getByte(0);System.out.println("aByte2 = " + aByte2);}
- duplicate() : 复制: 创建一个ByteBuf共享同一个内存
- composite() : 复合缓冲区: 将小的ByteBuf组合成一个大的ByteBuf,,不会进行数据的拷贝
java"> public static void main(String[] args) {// duplicate// 跟原始的ByteBuf用的同一个内存,,,也是零拷贝// copy 底层内存数据是深拷贝// 将小的ByteBuf 组合成一个 大的ByteBuf ,,也不会进行数据的拷贝ByteBuf buf1 = ByteBufAllocator.DEFAULT.buffer();buf1.writeBytes(new byte[]{1,2,3,4,5});ByteBuf buf2 = ByteBufAllocator.DEFAULT.buffer();buf2.writeBytes(new byte[]{6,7,8,9,10});buf2.retain();CompositeByteBuf buffer = ByteBufAllocator.DEFAULT.compositeBuffer();// 默认不会去调整写入指针位置,,需要设置 自动增长写指针buffer.addComponents(true,buf1,buf2);// 避免释放内存误操作,,需要用retain()添加引用计数buf2.release();System.out.println("buffer = " + buffer);byte aByte = buffer.getByte(9);System.out.println("aByte = " + aByte);}
ByteBuf的工具类 Unpooled
Unpooled.wrappedBuffer()
组合两个ByteBuf
java"> public static void main(String[] args) {// unpooled是一个工具类 : 非池化的ByteBuf 的创建,组合,复制等操作,,,ByteBuf buf1 = ByteBufAllocator.DEFAULT.buffer();buf1.writeBytes(new byte[]{1,2,3,4,5});ByteBuf buf2 = ByteBufAllocator.DEFAULT.buffer();buf2.writeBytes(new byte[]{6,7,8,9,10});// 当ByteBuf超过一个时,,底层使用了compositeByteBufByteBuf buf3 = Unpooled.wrappedBuffer(buf1, buf2);System.out.println("buf3 = " + buf3);byte aByte = buf3.getByte(9);System.out.println("aByte = " + aByte);}