优化点
netty重写了Java的ByteBuff,具体有哪些优点
- pooling池化,减少了内存的复制和GC。提升了效率
- 复合缓冲区,支持0复制
- 不需要调用flip()的方法切换读写。
- 可以自定义缓冲区类型(Heap Buff 和direct buff)。
- 可以进行引用计数。来标记是否可GC
ByteBuf的逻辑部分
分为4个部分
- 已用字节:表示已经使用完的无用的字节
- 可读字节:这部分是ByteBuf保存的有效数据,从ByteBuf中读取的数据都是这部分数据
- 可写字节:写入到ByteBuf中的数据都是这一部分数据。
- 可扩容字节:表示该ByteBuf最多还能扩容多少字节
ByteBuf的重要属性
- readerIndex(读指针):指示读的起始位置。每读取1个字节,readerIndex 自动+1,当readerIndex==writeIndex时,表示不可读
- writeIndex(写指针):指示写的起始位置。每写1个字节,writeIndex自动+1,当 writeIndex==capacity()
表示不可写,但是netty会自动扩容,capacity()是一个成员方法,不是最大容量。 - maxCapacity(最大容量):当前ByteBuf最大可容纳的字节数量。当超过最大容量时,抛异常
ByteBuf的三组方法
-
容量系列
capacity():表示ByteBuf的容量,它是 废弃的字节数+可读的字节数+可写的字节数 之和maxCapacity():表示ByteBuf的最大容量,当向ByteBuf中写入数据的时候,如果发现当前容量不足,
则自动扩容,直到扩容到maxCapacity设定的上限 -
写入系列
isWritable():是否可写
writableBytes():可写入的字节数
writableBytes(byte[] bytes):把bytes中的字节全部写入到 ByteBuf中
writeType(TYPE value):写入基本数据类型 -
读取系列:
isReadable():是否可读
readableBytes():当前可读的字节数
readBytes(byte[] bytes):读取bytes中的字节
readType():读取的数据类型,可以读取8大基本类型
ByteBuf的引用计数
ByteBuf引入计数器来对ByteBuf进行GC回收
当创建完一个ByteBuf的时候,它的引用就为1,每次调用retain()方法时,它的引用就+1,每次调用release()时,它的引用就-1
当引用为0的时候,表示这个ByteBuf可被GC回收。
为了确保引用计数不会混乱,在netty的业务处理器开发过程中,应该坚持1个原则:
retain和release方法应该结对使用,在一个方法中调用一次retain(),就应该调用一次release();
netty4.0的版本默认是非池化,但是也可设置池化。4.1的版本默认是池化
当引用计数器为0,netty会进行ByteBuf的回收,分2种情况:
- Pooled池化,回收方法是:放入可重新分配的ByteBuf池中,等待下次分配
- Unpooled 非池化的ByteBuf缓冲区,回收分2中
1)如果是堆(Heap)结构缓冲,会被JVM的垃圾回收机制回收。
2)如果是直接内存(Direct)类型,调用本地方法释放外部内存(unsafe.freeMemory)
ByteBuf的分配(Allocator分配器)
netty提供了ByteBufAllocator分配器的两种实现:
- PoolByteBufAllocator 池化分配器(netty4.1 版本默认)
- UnpooledByteBufAllocator 非池化分配器(netty4.0及以前的版本默认创建)
缓冲区类型:
这三种缓冲区在分配的时候,都可以分配成池化和非池化的。
直接缓冲区(Direct ByteBuf):
- 不属于java堆内存,所分配的内存是调用操作系统malloc()函数获得的。由netty的本地内存堆native堆进行管理
- 容量可指定,如果不指定,默认与Java堆大小相等
- Direct Memory避免了Java堆与native堆的来回复制,在某些应用场景下,提高了性能
- Direct Buf的读写比Heap Buf快,但是创建和销毁比较慢,所以直接缓冲区适合在池化的情况下使用
- 在需要频繁的创建和销毁缓冲区的场合,由于创建和销毁直接缓冲区(Direct Buffer)的代价比较高昂,所以不建议使用
- 创建直接缓冲区:ByteBufAllocator.DEFAULT.directBuffer();
ByteBuf的释放
分2种情况
- 自动释放:通道流水线channelPipeline的最后一个节点是TailHandler,在这个节点自动释放
- 手动释放: 通道流水线截断时,需要调用byteBuf.release(),来手动释放