netty中ByteBuf部分的分析

本文概述了Netty中Buffer组件的核心概念、关键类及其在实际编程中的应用,包括Buffer的两种主要类型(UnpooledDirectByteBuf与UnpooledHeapByteBuf)、ByteBuf抽象类及其丰富的API、DiscardReadBytes方法、读写操作的灵活性以及生成Buffer对象的方式。重点阐述了如何在Netty环境中高效地使用Buffer,特别关注了Buffer的池化管理和内存分配策略。

好久木有看代码了,今天把以前读netty源码的时候一直没有看的Buffer部分粗略的看了下,刚开始主要是觉得buffer这个包里面类太多了,觉得比较麻烦,而且相对理解整个netty的设计不太影响,所以就拖着没看,但是记得有一次跟支付宝的技术问了我关于netty中buffer的问题,自己回答的不太好,嗯,该看的还是要看的。。。。

嗯,还是先来一张比较重要的类图:



嗯,上面最后两个类型,UnpooledDirectByteBuf以及UnpooledHeapByteBuf就是netty中最终最常用到的两个buf类型,从名字就能够看出来他们有什么区别,其中一种使用到的内存是在JVM的堆空间上面分配的,其实底层说白了就是一个byte数组,而Direct也就是使用的直接内存,最终它也是在底层用的其实是java的nio中的bytebuffer。


最上层的ByteBuf是一个抽象类,它定义了很多的抽象方法,这些方法就是netty中bytebuf暴露的所有api,功能还是蛮强大的,netty之所以自己重头定义了整套的bytebuf就是因为nio类库中原生的buffer功能太弱的原因。。。。

这里面比较有意思的方法有如下:

(1)discardReadBytes,它用于对buf里面的数据进行平移,将已经读取的数据抛弃,并重用这部分内存空间,用如下这张图来说明:



(2)它定义的read和write方法中,有多种多样的源以及目的类型,甚至还有channel类型,这就意味着在buf中还是实现从流或者从channel中读取数据的具体操作。。。。

剩下的就还有很多buf常规的方法了,例如读取一个字节,读取一个整形什么的。。。。



好了,上面介绍了最终在netty中最常用的两种buf类型,从名字也可以看出他们不是pool的,在netty中还有一种pool的,实现的比较复杂,但是当然这种效率很高了,不过在实际的代码中基本上还是用的unpool的,好啦,而且除了在最开始unsafe对象中从channel中读取数据的时候常用directbuf外,其余一般都还是用的undirect的内存。。。


那么我们在编程的时候怎么使用buf呢?一般情况下都是使用一个工具类Unpooled,它定义了很多静态方法,编程时可以用它来生成buf,例如我们经常用如下的形式来生成一个bytebuf对象:

Unpooled.buffer(1000);

嗯,这段代码的意思就是生成1000个字节的buf对象,这里默认是分配undirect的内存,如果要分配direct的,那有专门的方法供调用。。。。


但是其实Unpooled最终也是调用ByteBufAllocator来创建buf对象的,他们之间的关系如下:



这里ByteBufAllocator对象默认是UnpooledByteBufAllocator对象,我们可以来看看它的类型定义:

public final class UnpooledByteBufAllocator extends AbstractByteBufAllocator {

    /**
     * Default instance
     */
    public static final UnpooledByteBufAllocator DEFAULT =
            new UnpooledByteBufAllocator(PlatformDependent.directBufferPreferred());

    /**
     * Create a new instance
     *
     * @param preferDirect {@code true} if {@link #buffer(int)} should try to allocate a direct buffer rather than
     *                     a heap buffer
     */
    public UnpooledByteBufAllocator(boolean preferDirect) {
        super(preferDirect);
    }

    @Override
    protected ByteBuf newHeapBuffer(int initialCapacity, int maxCapacity) {
        return new UnpooledHeapByteBuf(this, initialCapacity, maxCapacity);
    }

    @Override
    protected ByteBuf newDirectBuffer(int initialCapacity, int maxCapacity) {
        if (PlatformDependent.hasUnsafe()) {
            return new UnpooledUnsafeDirectByteBuf(this, initialCapacity, maxCapacity);
        } else {
            return new UnpooledDirectByteBuf(this, initialCapacity, maxCapacity);
        }
    }

    @Override
    public boolean isDirectBufferPooled() {
        return false;
    }
}

其实还是蛮简单的,也主要是实现了两个生成buf的方法,分别是direct和undirect的。。。。另外它导出了一个static的本类型对象,七班情况下也就是使用这个对象来分配buf对象。。。

另外在它的父类中定义了很多的分配方法,最基本的有创建heap的buf,也就是undirect的,还有创建direct的buf,另外还有自适应的方法,通过判断当前的系统类型,自动的来创建这两种类型的buf中的一种。。。。


另外UnpooledByteBufAllocator其实还是channel的默认的buf分配器。。。来看如下的这段代码:

 final ByteBufAllocator allocator = config.getAllocator();  //buffer的allocater
            final int maxMessagesPerRead = config.getMaxMessagesPerRead();

            boolean closed = false;
            Throwable exception = null;
            ByteBuf byteBuf = null;
            int messages = 0;
            try {
                for (;;) {
                    byteBuf = allocHandle.allocate(allocator);
                    int localReadAmount = doReadBytes(byteBuf); //将数据读进来

这段代码就是在channel可以读取的情况的时候,调用内部的unsafe对象来读取数据的时候要做的操作,这里首先是获取ByteBufAllocator对象,待会将会用它来生成用于存放这次读出来的数据buf,而这最终将会调用UnpooledByteBufAllocator自适应的方法来生成buf,最终这里一般情况下生成的是direct类型的buf。。。。。


好啦,到这里其实对netty中buf的设计有了比较粗略的大致了解,不再是以前那种模模糊糊的了。。。

总的来说实现的还算比较的简单吧。。。。而且用起来也还算是比较的方便。。。

### NettyByteBuf 的用法及常见问题解析 #### 1. ByteBuf 的基本概念 ByteBufNetty 提供的一种高效缓冲区管理工具,用于替代 JDK 自带的 ByteBuffer。它具有更灵活的功能设计和更高的性能表现[^1]。 #### 2. 创建 ByteBuf 实例 以下是创建 ByteBuf 的简单示例代码: ```java public class NettyByteBufExample { public static void main(String[] args) { // 默认分配器创建 ByteBuf 对象 ByteBuf buf = ByteBufAllocator.DEFAULT.buffer(); System.out.println(buf); } } ``` 上述代码展示了如何通过 `ByteBufAllocator` 来获取默认的 ByteBuf 实例[^2]。 #### 3. ByteBuf 的存储结构 ByteBuf 的内部存储结构分为三个主要部分:读索引 (`readerIndex`)、写索引 (`writerIndex`) 和容量 (`capacity`)。这些字段共同决定了数据的操作范围和边界。 #### 4. 控制池化功能 Netty 支持通过环境变量配置是否启用内存池机制。如果希望禁用内存池,则可以通过设置如下参数实现: ```properties -Dio.netty.allocator.type=unpooled ``` 反之,若要启用内存池,则将值设为 `pooled` 即可。 #### 5. 高度可扩展性 ByteBuf 接口允许开发者根据实际需求对其进行扩展或自定义实现。例如,在某些特殊场景下可能需要增加额外的方法或属性支持,此时可通过继承现有类或者重新实现接口完成定制开发工作[^4]。 #### 6. 内存泄漏检测 为了防止因不当操作而导致资源未释放而引发的潜在风险,Netty 设计了一套完善的内存泄露监控体系。当发现异常情况时会自动记录相关信息以便排查问题所在位置[^3]。 --- ### 示例代码展示 以下是一个简单的 ByteBuf 使用案例: ```java import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; public class SimpleByteBufUsage { public static void main(String[] args) { // 创建一个非池化的 ByteBuf ByteBuf buffer = Unpooled.buffer(10); // 向 Buffer 写入字节数据 String message = "Hello"; byte[] bytes = message.getBytes(); buffer.writeBytes(bytes); // 输出当前状态信息 System.out.println("Reader Index: " + buffer.readerIndex()); System.out.println("Writer Index: " + buffer.writerIndex()); // 移动 Reader Index 并读取第一个字符 char firstChar = (char) buffer.readByte(); System.out.println("First Character: " + firstChar); // 清理资源 buffer.release(); } } ``` 此程序演示了如何向 ByteBuf 写入字符串并从中提取单个字符的过程。 --- ###
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值