读书笔记:《Netty进阶之路》——netty缓冲区、缓冲区释放、内存池

  第3章提出问题:“缓冲区未正确释放导致Netty内存池泄露”,引出Netty内存管理方式介绍;第4张提出问题:“ByteBuf使用不当导致报错、程序无法正常运行”,引出ByteBuf的使用方式和原理。我颠倒一下顺序,先回顾ByteBuf的正确使用和实现原理,后回顾Netty内存池技术。

1 Netty缓冲区——ByteBuf

  Netty的ByteBuf是一个抽象类,是Netty管辖的内存,里面主要是一些byte数组的读写方法,与其说是抽象类,不如说它是一个接口,因为它里面几乎全是抽象方法,下面是截取它的一部分方法:
在这里插入图片描述在这里插入图片描述
  ByteBuf有四个重要的实现类:PooledDirectByteBuf,PooledHeapByteBuf, UnpooledDirectByteBuf,UnpooledHeapByteBuf, 它们分别代表池化直接内存,池化堆内存,未池化直接内存,未池化堆内存。直接内存代表没有数据从内核空间到用户空间的复制,代表高效;池化代表高效的内存分配和释放。

1.1 内存分配

  四种内存分配:

        // 未池化堆内存
        ByteBuf heapByteBuf = Unpooled.buffer(10);
        // 未池化直接内存
        ByteBuf directByteBuf = Unpooled.directBuffer(10);
        // 池化堆内存
        PooledByteBufAllocator allocator = new PooledByteBufAllocator(false);
        ByteBuf pHeapByteBuf = allocator.buffer();
        // 池化直接内存
        PooledByteBufAllocator allocator2 = new PooledByteBufAllocator(true);
        ByteBuf pDirectByteBuf = allocator.buffer();

  复合内存是把上述几种内存复合成一个对象来操作的一种内存。

        CompositeByteBuf compositeByteBuf = Unpooled.compositeBuffer();
        compositeByteBuf.addComponents(heapByteBuf, directByteBuf, pHeapByteBuf, pDirectByteBuf);
1.2 内存读写操作

ByteBuf 有两个游标,readerIndex指向已读字节位,writerIndex指向已写字节位。通常我们不会对同一个ByteBuf一边读一边写,因此可以理解为读操作时只存在readerIndex一个游标,写操作时只存在writerIndex一个游标。这两个游标会随着我们的读写操作自动变换位置,也可以手动强行把它们移动到指定位置。

读示例:
    // 读取前判断可读状态
    if (heapByteBuf.isReadable()) {
        // 计算可读长度
        int len = heapByteBuf.readableBytes();
        // 可以直接读出到ByteBuf中
        ByteBuf read = heapByteBuf.readBytes(len/2);
        // 也可读出到指定byte数组
        byte[] dis = new byte[len/2];
        heapByteBuf.readBytes(dis);
    }
写示例:
        // 写前判断可写余量
        if (heapByteBuf.isWritable()){
            // XXX可以是byte[] 和 ByteBuf 
            heapByteBuf.writeBytes(XXX);
        }
1.3 内存释放

  ByteBuf内存释放: ByteBuf抽象类实现了ReferenceCounted接口,实现引用计数功能。引用计数为0时可以释放:

        directByteBuf.release();

  netty在有些地方会自动释放ByteBuf,在有些地方又不会,总结3.1.4的内容:只有未继承SimpleChannelInboundHandler的响应ByteBuf需要手动在业务代码中使用ReferenceCountUtil.release(byteBuf)释放,其他情况,不论是否池化,不论请求还是响应,都由Netty自动释放。

2 Netty 内存池

  Netty默认使用内存池创建ByteBuf。
在这里插入图片描述
  上图内涵丰富:①从左到右,申请内存时PooledByteBufAllocator优先使用线程绑定的线程池PoolThreadLocalCache防止线程争用,如果没有则向poolArena申请,申请成功后绑定到线程。②右侧从上到下,若需要的内存大小x>Chuck尺寸则从Huge分配,chuck>x>page时从PoolChuck分配(也就是图上的二叉树),若x<page则把一个Page平分成Page/x份再分配。③图中的二叉树,每个叶结点对应一个Page,所有节点都保存2个信息,分别属于memoryMap和depthMap,depthMap信息不变,变化的是memoryMap的信息也就是左边的数字:内存被分配则叶子结点其值+1,其父节点值取子节点最小值,往上递归。举例:若某次内存分配过程中PoolSubPage1、PoolSubPage2和PoolSubPage3被分配,则节点memoryMap信息更新如下:
在这里插入图片描述
  根据规则,图中A、B、C、E左边的值=右边值+1,说明这几个节点映射的Page内存已经被分配完,D、F、G则还没有分配完。一共有3条规则:左边=右边表示可分配,左边=右边+1表示不可分配,左边>右边但不满足第2条时表示部分可分配。
  内存池的优势:根据书中3.2.1的实验,池化比非池化的分配和释放性能高出8倍以上。

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值