上文已经了解到了Netty中的启动器、反应器、通道、处理器、流水线,下面来了解一下Netty中较为重要的ByteBuf缓冲区。
Netty采用“计数器”来追踪ByteBuf的生命周期,主要是用于对池化的支持。(池化就是当ByteBuf的引用为0时,就会放到对象缓存池中,当需要用缓冲区时,可以直接从这个池里面取出来用,而不用重新创建一个了)。通过源码可以发现ByteBuf实现了一个类ReferenceCounted。这个类就是用于引用计数的。
当创建完ByteBuf时,引用数为1, 通过refCnt()方法可以获取当前缓冲区的引用数,调用retain()方法可以使引用数加1,调用release()方法可以使引用数减1,当引用数为0时,缓冲区就会被完全释放。如果池化了就放到缓冲池中。如果没池化就分两种情况,如果是分配在堆内存上的,就通过JVM的垃圾回收机制把它回收,如果分配在堆外直接内存上,就通过本地方法来释放堆外内存。在Handler处理器中,Netty会自动给流水线在最后加一个处理器用来调用release()去释放缓冲区,如果要在中间中断流水线,则需要自己调用release()释放缓冲区。
Netty提供了ByteAllocator的两种实现:PoolByteAllocator(池化)和UnpooledByteAllocator(未池化)。Netty默认使用的是PoolByteAllocator,默认使用的内存是堆外直接内存(写入速度比堆内存更快,池化分配器配合堆外直接内存,可将堆外缓冲区复用(弥补了堆外分配和释放空间的代价较高的缺点),从来大大提升了性能)。
浅层复制有两个方法,切片浅层复制和整体浅层复制
来写个小例子,分析一下。这里的Logger使用的是自己封装的静态日志类。 分析堆栈信息封装一个SLF4J的静态类
public class testBuffer { public static void main(String[] args) { // 默认使用池化缓冲区,分配的是堆外直接内存 ByteBuf buf = ByteBufAllocator.DEFAULT.buffer(9, 100); // 使用堆内存来分配缓冲区内存 ByteBuf buf1 = ByteBufAllocator.DEFAULT.heapBuffer(9, 100); // 写入一个字节数组 buf.writeBytes(new byte[] { 1, 2, 3, 4 }); Logger.info("引用次数--[{}]", buf.refCnt()); // 依次读出来 int i = 0; while (buf.isReadable() && i < buf.readableBytes()) { // 读字节,不改变指针(读完后,readIndex还是0) Logger.info("取一个字节--[{}]", buf.getByte(i++)); } Logger.info("buf是否使用的堆内存--[{}]", buf.hasArray()); Logger.info("buf1是否使用的堆内存--[{}]", buf1.hasArray()); int len = buf.readableBytes(); byte[] array = new byte[len]; // 把数据读取到堆内存中 buf.getBytes(buf.readerIndex(), array); Logger.info("buf读出的数据--[{}]", array); // 与原缓冲区buf的底层引用一样 ByteBuf slice = buf.slice(); // 增加一次浅层复制的引用 slice.retain(); // 减少一次原缓冲区的引用 buf.release(); // 会发现两个引用是同一个 Logger.info("引用次数--[{}]", buf.refCnt()); Logger.info("切片结果--{}", slice); Logger.info("引用次数--[{}]", slice.refCnt()); } } 复制代码
运行结果:
21:02:59.693 [main] INFO byteBuf.test1 - 引用次数--[1] 21:02:59.695 [main] INFO byteBuf.test1 - 取一个字节--[1] 21:02:59.696 [main] INFO byteBuf.test1 - 取一个字节--[2] 21:02:59.696 [main] INFO byteBuf.test1 - 取一个字节--[3] 21:02:59.696 [main] INFO byteBuf.test1 - 取一个字节--[4] 21:02:59.696 [main] INFO byteBuf.test1 - buf是否使用的堆内存--[false] 21:02:59.696 [main] INFO byteBuf.test1 - buf1是否使用的堆内存--[true] 21:02:59.696 [main] INFO byteBuf.test1 - buf读出的数据--[[1, 2, 3, 4]] 21:02:59.698 [main] INFO byteBuf.test1 - 引用次数--[1] 21:02:59.698 [main] INFO byteBuf.test1 - 切片结果--UnpooledSlicedByteBuf(ridx: 0, widx: 4, cap: 4/4, unwrapped: PooledUnsafeDirectByteBuf(ridx: 0, widx: 4, cap: 9/100)) 21:02:59.698 [main] INFO byteBuf.test1 - 引用次数--[1] 复制代码