重学Netty(五)——细说一下ByteBuf

在上篇文章也提到ByteBuf和它的几种模式,下面就来了解一下ByteBuf的其他方面,及其优点。还有一些关于ByteBuf池化的一些问题。

ByteBuf的几种获取方式

在Netty中可以通过下面三种方法来获取ByteBuf

ByteBufAllocator

可以使用ByteBufAllocator来进行Buffer的按需分配,可以自己定义大小,也可以自己决定是否使用Buffer内存池。
ByteBufAllocator接口的方法如下,可以看到对任意类型的Buffer都可以进行分配操作,也可以规定Buffer的大小,不知道有没有发现它的DEFAULT属性,这个是决定它使用还是不使用内存池的关键,默认是使用内存池
在这里插入图片描述
对于DEFAULT进行源码跟踪,如下标注的序号所示,可以看到序号3是对DEFALUT_ALLOCATOR进行了赋值操作,它的值是从序号1来的,如果是安卓平台会采用非池化,非安卓平台就采用的是池化,序号2只是根据序号1得到的type进行特征值的获取
在这里插入图片描述

因为ByteBufAllocator是一个interface,因此我们可以瞧一瞧实现了这个接口的类,可以看到除了抽象ByteBufAllocate,会有一个Pooled和Unpooled,这个就是决定你分配Buffer的时候是否采用内存池。内存池的优点就是可以提高性能,也可以最大限度的减少内存碎片
在这里插入图片描述
因为ByteBufAllocator中含有各种的Buffer分配的方法,因此它的子类都对此做了实现,可以使用这些类来创建ByteBuf

在这里插入图片描述在这里插入图片描述

这个是基于ByteBufAllocator的一种方法,ByteBufAllocator的获取不仅仅可以对Pooled和Unpooled实例化获取,也可以从Channel中获取,如下
在这里插入图片描述

使用ByteBufAllocator是一种可以比较灵活的分配ByteBuf的一种方式。

Unpooled

在有的时候不能获取到ByteBufAllocator的引用,或者不必要去实例化一个ByteBufAllocator的子类,Netty提供了一种更简单的方法,可以使用Unpooled类,因此它是非内存池化的Buffer。
主要的方法如下
在这里插入图片描述
这里面这个复制Buffer的方法还是常常用到的。

ByteBufUtil

可以从类名看出是一个操作ByteBuf的工具类,它是不能直接的创建Buffer,但是可以通过操作数据或者Buffer来获得Buffer。它里面有很多很实用的方法,在平常开发中可以减少写一些重复的代码。
在这里插入图片描述
比如它可以以16进制的形式表示Buffer中的内容,当然也可以转回去。也可以和规定字符集的字符串进制转换。还有一些Buffer的copy操作等等

一些派生缓冲区

可以通过获取指定Buffer的派生缓冲区

  • duplicate()
  • slice() 或 slice(int, int)
  • Unpooled.unmodifiableBuffer(…)
  • order(ByteOrder)
  • readSlice(int)

什么是派生缓冲区,它与原ByteBuf共享内存区域,它的修改也会影响原来的Buffer,如下代码

public class Allocator {
    public static void main(String[] args) {

        Charset charset = Charset.forName("UTF-8");
        ByteBuf byteBuf = Unpooled.copiedBuffer("study netty", charset);
        System.out.println("byteBuf:"+byteBuf.toString(charset));

        ByteBuf duplicate = byteBuf.duplicate();
        System.out.println("duplicate: "+duplicate.toString(charset));

        ByteBuf slice = duplicate.slice(0, 8);
        System.out.println("slice duplicate: "+slice.toString(charset));

        slice.setByte(7, 'a');
        System.out.println("操作slice set 7 -> a");
        System.out.println("byteBuf:"+byteBuf.toString(charset));
        duplicate.setByte(8, 'b');
        System.out.println("操作duplicate set 8 -> b");
        System.out.println("byteBuf:"+byteBuf.toString(charset));

    }

}

运行结果如下图所示,因此可以看出这些派生缓冲区不同于copy操作的缓冲区,copy的缓冲区只是一个副本。
在这里插入图片描述

ByteBufHolder

从接口名可以看出它的含义,也就是ByteBuf的持有者,也就是相当于C原因中的结构体,ByteBuf就是这个结构体中的主要内容。它可以将ByteBuf包装在content里面。就和Spring源码中的BeanDefinitionHolder是一样的,它里面不仅仅需要存BeanDefinition,还要存beanName。ByteBufHolder中不仅仅要存ByteBuf,还要存一些其他内容。
它可以从缓冲区池中借用ByteBuf,在不需要的时候可以释放。
ByteBufHolder的一些操作
在这里插入图片描述

说一说引用计数

学过Redis的都知道Redis的对象存活判断就是采用的引用计数法,引用计数法实现简单,处理高效,缺点是无法解决循环引用问题。在Netty的Buffer池中也的对象释放时机,也是使用的引用计数法。
如下图无论是ByteBuf还是ByteBufHolder都是ReferenceCounted的子类,因此可以更进一步的判断是采用的引用计数法。
在这里插入图片描述
在这里插入图片描述
当计数为0时就表明此Buffer实例要被释放(可能不是真正的释放,但是是不可再用了),一般由最后一个访问该对象的哪一方来进行释放操作

可以看出Netty对ByteBuf的实现还是考虑了很多的。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值