Netty学习——源码篇10 Netty内存分配ByteBuf基础

时间:2024-04-03 19:28:21

1 初始ByteBuf

        ByteBuf是Netty整个结构中最为底层的模块,主要负责把数据从底层I/O读取到ByteBuf,然后传递给应用程序,应用程序处理完成后再把数据封装成ByteBuf写回I/O。所以,ByteBuf是直接与底层打交道的一层抽象。

2 ByteBuf的基本结构

        Netty官方对ByteBuf的描述,具体如下:

        从上面ByteBuf的结构来看,发现ByteBuf有三个非常重要的指针,分别是readerIndex(记录读指针的开始位置)、writerIndex(记录写指针的开始位置)和capacity(缓冲区的总长度),三者的关系是:readerIndex <= writerIndex <= capacity。从0到readerIndex为discardable bytes,表示是无效的;从readerIndex 到 writerIndex 为readable bytes,表示可读数据区;从writerIndex 到capacity位writable bytes,表示这段区间空闲,可以往里面写数据。除了这三个指针,ByteBuf里面其实还有一个指针maxCapacity,它相当于ByteBuf扩容的最大阈值,相应代码如下。

    /**
     * Returns the maximum allowed capacity of this buffer.  If a user attempts to increase the
     * capacity of this buffer beyond the maximum capacity using {@link #capacity(int)} or
     * {@link #ensureWritable(int)}, those methods will raise an
     * {@link IllegalArgumentException}.
     */
    public abstract int maxCapacity();

        这个maxCapacity指针可以看作是指向capacity之后的这段区间,Netty发现writable bytes写数据超出空间大小时,ByteBuf会提前自动扩容,扩容之后,就有了足够的空间来写数据,同时capacity也会同步更新,maxCapacity就是扩容后capacity的最大值。

3 ByteBuf的重要API

        看一下ByteBuf的基本API,主要包括read()  write()  set()  mark() reset()等方法。请看下表

        在Netty中,ByteBuf大部分功能是在AbstractByteBuf中实现的。

public abstract class AbstractByteBuf extends ByteBuf {
    //读指针
    int readerIndex;
    //写指针
    int writerIndex;
    //mark之后的读指针
    private int markedReaderIndex;
    //mark之后的写指针
    private int markedWriterIndex;
    //最大容量
    private int maxCapacity;

    ...
}

        下面来看基本读写的骨架代码实现。例如,几个基本的判断读写区间的API,具体实现代码如下:

public abstract class AbstractByteBuf extends ByteBuf {
@Override
    public boolean isReadable() {
        return writerIndex > readerIndex;
    }

    @Override
    public boolean isReadable(int numBytes) {
        return writerIndex - readerIndex >= numBytes;
    }

    @Override
    public boolean isWritable() {
        return capacity() > writerIndex;
    }

    @Override
    public boolean isWritable(int numBytes) {
        return capacity() - writerIndex >= numBytes;
    }

    @Override
    public int readableBytes() {
        return writerIndex - readerIndex;
    }

    @Override
    public int writableBytes() {
        return capacity() - writerIndex;
    }

    @Override
    public int maxWritableBytes() {
        return maxCapacity() - writerIndex;
    }

    @Override
    public ByteBuf markReaderIndex() {
        markedReaderIndex = readerIndex;
        return this;
    }

    @Override
    public ByteBuf resetReaderIndex() {
        readerIndex(markedReaderIndex);
        return this;
    }

    @Override
    public ByteBuf markWriterIndex() {
        markedWriterIndex = writerIndex;
        return this;
    }

    @Override
    public ByteBuf resetWriterIndex() {
        writerIndex = markedWriterIndex;
        return this;
    }
}

4 ByteBuf基本分类

        AbstractByteBuf有很多子类,大致可以从三个维度进行分类。

        1、Pooled:池化内存,就是从预先分配好的内存空间中提取一段连续内存封装成一个ByteBuf,分给应用程序使用。

        2、Unsafe:是JDK底层的一个负责I/O操作的对象,可以直接获得对应的内存地址,基于内存地址进行读写操作。

        3、Direct:堆外内存,直接调用JDK底层API进行物理内存分配,不在JVM的堆内存中,需要手动释放。

        综上所述,其实ByteBuf会有6种组合:Pooled(池化内存)和Unpooled(非池化内存);Unsafe和非Unsafe;Heap(堆内存)和Direct(堆外内存)。下图是ByteBuf最重要的继承关系类结构图,通过命名就能一目了然。

        ByteBuf最基本的读写API操作在AbstractByteBuf中已经实现了,其众多子类采用不同的策略来分配内存空间,下表是对重要的几个子类的总结。

 5 ByteBufAllocator内存管理器

        Netty中内存分配有一个顶层的抽象就是ByteBufAllocator,负责分配所有ByteBuf类型的内存。主要有几个重要的API,如下表:

        以上API中为什么没有前面提到的8种类型的内存分配API?下面来看ByteBufAllocator的基本实现类AbstractByteBufAllocator,重点分析主要API的基本实现,比如buffer()方法的代码如下:

public abstract class AbstractByteBufAllocator implements ByteBufAllocator {
    public ByteBuf buffer() {
        if (directByDefault) {
            return directBuffer();
        }
        return heapBuffer();
    }
}

         发现buffer方法中对是否默认支持directBuffer做了判断,如果支持则分配directBuffer,否则分配heapBuffer。

        下面分别来看directBuffer方法和heapBuffer方法的实现,先来看directBuffer方法的代码。

    @Override
    public ByteBuf directBuffer() {
        return directBuffer(DEFAULT_INITIAL_CAPACITY, Integer.MAX_VALUE);
    }

    @Override
    public ByteBuf directBuffer(int initialCapacity) {
        return directBuffer(initialCapacity, Integer.MAX_VALUE);
    }

    @Override
    public ByteBuf directBuffer(int initialCapacity, int maxCapacity) {
        if (initialCapacity == 0 && maxCapacity == 0) {
            return emptyBuf;
        }
        validate(initialCapacity, maxCapacity);
        return newDirectBuffer(initialCapacity, maxCapacity);
    }

        directBuffer方法有多个重载方法,最终会调用newDirectBuffer方法,下面看newDirectBuffer的代码。

protected abstract ByteBuf newDirectBuffer(int initialCapacity, int maxCapacity);

        发现newDirectBuffer方法其实是一个抽象方法,最终,交给AbstractByteBufAllocator的子类来实现。同理再来看heapBuffer方法的代码。

   @Override
    public ByteBuf heapBuffer() {
        return heapBuffer(DEFAULT_INITIAL_CAPACITY, Integer.MAX_VALUE);
    }

    @Override
    public ByteBuf heapBuffer(int initialCapacity) {
        return heapBuffer(initialCapacity, Integer.MAX_VALUE);
    }

    @Override
    public ByteBuf heapBuffer(int initialCapacity, int maxCapacity) {
        if (initialCapacity == 0 && maxCapacity == 0) {
            return emptyBuf;
        }
        validate(initialCapacity, maxCapacity);
        return newHeapBuffer(initialCapacity, maxCapacity);
    }

        发现heapBuffer方法最终是调用newHeapBuffer方法,而newHeapBuffer方法也是抽象方法,具体交给AbstractByteBufAllocator的子类实现。AbstractByteBufAllocator的子类主要有两个:PooledByteBufAllocator和UnpooledByteBufAllocator。AbstractByteBufAllocator的子类实现的类结构图如下:

        分析到这里,已经知道directBuffer,heapBuffer和Pooled和Unpooled的分配规则,那么Unsafe和非Unsafe是如何判别的呢?其实是Netty自动判别的。如果操作系统底层支持Unsafe那就采用Unsafe读写,否则采用非Unsafe读写。可以从UnpooledByteBufAllocator的源码中验证,代码如下:

public final class UnpooledByteBufAllocator extends AbstractByteBufAllocator {

    @Override
    protected ByteBuf newHeapBuffer(int initialCapacity, int maxCapacity) {
        return PlatformDependent.hasUnsafe() ? new UnpooledUnsafeHeapByteBuf(this, initialCapacity, maxCapacity)
                : new UnpooledHeapByteBuf(this, initialCapacity, maxCapacity);
    }

    @Override
    protected ByteBuf newDirectBuffer(int initialCapacity, int maxCapacity) {
        ByteBuf buf = PlatformDependent.hasUnsafe() ?
                UnsafeByteBufUtil.newUnsafeDirectByteBuf(this, initialCapacity, maxCapacity) :
                new UnpooledDirectByteBuf(this, initialCapacity, maxCapacity);

        return disableLeakDetector ? buf : toLeakAwareBuffer(buf);
    }
}

        发现在newHeapBuffer方法和newDirectBuffer方法中,分配内存判断PlatformDependent是否支持Unsafe,如果支持则创建Unsafe类型的Buffer,否则创建非Unsafe类型的Buffer,由Netty自动判断。