首页 > 代码库 > Netty5源码分析(五) -- ByteBuf缓冲区

Netty5源码分析(五) -- ByteBuf缓冲区

Netty的ByteBuf缓冲区实现地比Java本身的ByteBuffer更加灵活,方便。它的类结构也比较复杂,这里只说ByteBuf核心的几个要点。


1. 最重要的是要理解为什么要ByteBuf这个组件。主要还是因为基于select / poll / epoll这种IO多路复用技术的NIO是非阻塞同步IO的模型,由于是同步IO,需要用户线程自己来处理IO的读写,由于是非阻塞的,每次调用read, write读写的字节数是不确定的,所以非阻塞同步IO必须有缓冲区这个组件来保存每次读写的中间状态,通过缓冲区来确定是否读写完成。更多内容请参考http://blog.csdn.net/iter_zc/article/details/39291647


2. ByteBuf不是对ByteBuffer的封装,而是重新实现了一个缓冲区。ByteBuffer只使用了一个position指针来记录当前的读写位置,ByteBuf使用了两个指针readerIndex, writerIndex分别来记录当前的读写位置,使用起来更加简单和方便。


3. ByteBuffer是一个固定长度的缓冲区,当put方法要写的数据大于可写的容量时会抛出异常。ByteBuf改进了这个设计,支持自动扩容。每次put之前会检查是否可以完全写入,如果不能,就会自动扩展ByteBuf的容量,保证put方法不会抛出异常。

public ByteBuf writeInt(int value) {
        ensureWritable(4);
        _setInt(writerIndex, value);
        writerIndex += 4;
        return this;
    }   public ByteBuf ensureWritable(int minWritableBytes) {        if (minWritableBytes < 0) {            throw new IllegalArgumentException(String.format(                    "minWritableBytes: %d (expected: >= 0)", minWritableBytes));        }        if (minWritableBytes <= writableBytes()) {            return this;        }        if (minWritableBytes > maxCapacity - writerIndex) {            throw new IndexOutOfBoundsException(String.format(                    "writerIndex(%d) + minWritableBytes(%d) exceeds maxCapacity(%d): %s",                    writerIndex, minWritableBytes, maxCapacity, this));        }        // Normalize the current capacity to the power of 2.        int newCapacity = calculateNewCapacity(writerIndex + minWritableBytes);        // Adjust to the new capacity.        capacity(newCapacity);        return this;    }//UnpooledHeapByteBuf的capacity方法来自动扩容public ByteBuf capacity(int newCapacity) {        ensureAccessible();        if (newCapacity < 0 || newCapacity > maxCapacity()) {            throw new IllegalArgumentException("newCapacity: " + newCapacity);        }        int oldCapacity = array.length;        if (newCapacity > oldCapacity) {            byte[] newArray = new byte[newCapacity];            System.arraycopy(array, 0, newArray, 0, array.length);            setArray(newArray);        } else if (newCapacity < oldCapacity) {            byte[] newArray = new byte[newCapacity];            int readerIndex = readerIndex();            if (readerIndex < newCapacity) {                int writerIndex = writerIndex();                if (writerIndex > newCapacity) {                    writerIndex(writerIndex = newCapacity);                }                System.arraycopy(array, readerIndex, newArray, readerIndex, writerIndex - readerIndex);            } else {                setIndex(newCapacity, newCapacity);            }            setArray(newArray);        }        return this;    }private void setArray(byte[] initialArray) {        array = initialArray;        tmpNioBuf = null;    }

4. 和ByteBuffer一样,ByteBuf也支持堆内缓冲区和堆外直接缓冲区,根据经验来说,底层IO处理线程的缓冲区使用堆外直接缓冲区,减少一次IO复制。业务消息的编解码使用堆内缓冲区,分配效率更高,而且不涉及到内核缓冲区的复制问题。


5. ByteBuf的堆内缓冲区又分为内存池缓冲区PooledByteBuf和普通内存缓冲区UnpooledHeapByteBuf。PooledByteBuf采用二叉树来实现一个内存池,集中管理内存的分配和释放,不用每次使用都新建一个缓冲区对象。UnpooledHeapByteBuf每次都会新建一个缓冲区对象。在高并发的情况下推荐使用PooledByteBuf,可以节约内存的分配。在性能能够保证的情况下,可以使用UnpooledHeapByteBuf,实现比较简单。


具体ByteBuf的API请参考文档。






Netty5源码分析(五) -- ByteBuf缓冲区