Netty之内存分配器ByteBufAllocator

目录

内存分配器继承体系

ByteBufAllocator

AbstractByteBufAllocator

UnpooledByteBufAllocator

newHeapBuffer

newDirectBuffer

PooledByteBufAllocator

PoolThreadLocalCache

PoolArena

directArena分配direct内存流程


内存分配器继承体系


ByteBufAllocator

ByteBufAllocator是Netty内存分配最顶层的抽象,负责分配所有类型的内存。

public interface ByteBufAllocator {

    ByteBufAllocator DEFAULT = ByteBufUtil.DEFAULT_ALLOCATOR;

    // 直接分配一块内存,是使用direct还是heap取决于子类实现
    ByteBuf buffer();

    // 重载方法,增加初始容量
    ByteBuf buffer(int initialCapacity);

    // 重载方法,增加初始容量与最大容量
    ByteBuf buffer(int initialCapacity, int maxCapacity);

    // 更倾向于direct方式分配
    ByteBuf ioBuffer();

    ByteBuf ioBuffer(int initialCapacity);

    ByteBuf ioBuffer(int initialCapacity, int maxCapacity);

    // heap内存分配
    ByteBuf heapBuffer();

    ByteBuf heapBuffer(int initialCapacity);

    ByteBuf heapBuffer(int initialCapacity, int maxCapacity);

    // direct内存分配
    ByteBuf directBuffer();

    ByteBuf directBuffer(int initialCapacity);

    ByteBuf directBuffer(int initialCapacity, int maxCapacity);
    ...
}

AbstractByteBufAllocator

AbstractByteBufAllocator是ByteBufAllocator的骨架实现(类似于AbstractByteBuf和ByteBuf的关系),它提供了两个重要的抽象类供子类扩展,以实现堆内和堆外内存的创建。

    // 创建堆内内存
    protected abstract ByteBuf newHeapBuffer(int initialCapacity, int maxCapacity);
    // 创建堆外内存
    protected abstract ByteBuf newDirectBuffer(int initialCapacity, int maxCapacity);

UnpooledByteBufAllocator

UnpooledByteBufAllocator是非池化分配器,这里着重分析一下newHeapBuffer方法与newDirectBuffer方法的实现逻辑。

    @Override
    protected ByteBuf newHeapBuffer(int initialCapacity, int maxCapacity) {
        // netty会优先使用unsafe方式
        return PlatformDependent.hasUnsafe() ? new UnpooledUnsafeHeapByteBuf(this, initialCapacity, maxCapacity)
                : new UnpooledHeapByteBuf(this, initialCapacity, maxCapacity);
    }

    @Override
    protected ByteBuf newDirectBuffer(int initialCapacity, int maxCapacity) {
        // netty会优先使用unsafe方式
        ByteBuf buf = PlatformDependent.hasUnsafe() ?
                UnsafeByteBufUtil.newUnsafeDirectByteBuf(this, initialCapacity, maxCapacity) :
                new UnpooledDirectByteBuf(this, initialCapacity, maxCapacity);

        return disableLeakDetector ? buf : toLeakAwareBuffer(buf);
    }

newHeapBuffer

在newHeapBuffer方法中,会根据hasUnsafe的值来确定ByteBuf的类型是UnpooledUnsafeHeapByteBuf还是UnpooledHeapByteBuf。

UnpooledUnsafeHeapByteBuf是UnpooledHeapByteBuf的子类,子类构造函数调用父类构造函数,走了同一套实例化的逻辑,通过new的方式创建了指定容量的byte数组。

final class UnpooledUnsafeHeapByteBuf extends UnpooledHeapByteBuf {
    UnpooledUnsafeHeapByteBuf(ByteBufAllocator alloc, int initialCapacity, int maxCapacity) {
        super(alloc, initialCapacity, maxCapacity);
    }
    ...
}

public class UnpooledHeapByteBuf extends AbstractReferenceCountedByteBuf {
    protected UnpooledHeapByteBuf(ByteBufAllocator alloc, int initialCapacity, int maxCapacity) {
        this(alloc, new byte[initialCapacity], 0, 0, maxCapacity);
    }
    ...

    private UnpooledHeapByteBuf(
            ByteBufAllocator alloc, byte[] initialArray, int readerIndex, int writerIndex, int maxCapacity) {

        super(maxCapacity);

        if (alloc == null) {
            throw new NullPointerException("alloc");
        }
        if (initialArray == null) {
            throw new NullPointerException("initialArray");
        }
        if (initialArray.length > maxCapacity) {
            throw new IllegalArgumentException(String.format(
                    "initialCapacity(%d) > maxCapacity(%d)", initialArray.length, maxCapacity));
        }

        this.alloc = alloc;
        setArray(initialArray);
        setIndex(readerIndex, writerIndex);
    }

    private void setArray(byte[] initialArray) {
        array = initialArray;
        tmpNioBuf = null;
    }
}

既然UnpooledUnsafeHeapByteBuf与UnpooledHeapByteBuf的实例化逻辑完全一样,那它们的区别在哪?

我们知道在父类AbstractByteBuf中有很多下划线开头的抽象方法,这些方法都是需要子类去实现的,两者的区别就是在_getByte方法的实现上。

UnpooledHeapByteBuf的_getByte方法是直接拿到初始化new的byte数组,通过index获取数据。

    @Override
    protected byte _getByte(int index) {
        return HeapByteBufUtil.getByte(array, index);
    }
    ...
    static byte getByte(byte[] memory, int index) {
        return memory[index];
    }

而UnpooledUnsafeHeapByteBuf的_getByte方法则是调用了unsafe对象的native API,根据数组对象与偏移量来获取数据,效率相对而言较高,这也是Netty优先选择unsafe方式的原因。

    @Override
    protected byte _getByte(int index) {
        return UnsafeByteBufUtil.getByte(array, index);
    }
    ...
    static byte getByte(byte[] array, int index) {
        return PlatformDependent.getByte(array, index);
    }
    ...
    public static byte getByte(byte[] data, int index) {
        return PlatformDependent0.getByte(data, index);
    }

    static byte getByte(byte[] data, int index) {
        return UNSAFE.getByte(data, BYTE_ARRAY_BASE_OFFSET + index);
    }

newDirectBuffer

在newDirectBuffer方法中,同样会根据hasUnsafe的值来ByteBuf的类型是否是Unsafe。

首先是UnpooledDirectByteBuf,它构造函数的逻辑很简单,通过堆外内存创建ByteBuffer。

    protected UnpooledDirectByteBuf(ByteBufAllocator alloc, int initialCapacity, int maxCapacity) {
        super(maxCapacity);
        if (alloc == null) {
            throw new NullPointerException("alloc");
        }
        if (initialCapacity < 0) {
            throw new IllegalArgumentException("initialCapacity: " + initialCapacity);
        }
        if (maxCapacity < 0) {
            throw new IllegalArgumentException("maxCapacity: " + maxCapacity);
        }
        if (initialCapacity > maxCapacity) {
            throw new IllegalArgumentException(String.format(
                    "initialCapacity(%d) > maxCapacity(%d)", initialCapacity, maxCapacity));
        }

        this.alloc = alloc;
        // ByteBuffer.allocateDirect(initialCapacity)通过堆外内存创建ByteBuffer
        setByteBuffer(ByteBuffer.allocateDirect(initialCapacity));
    }

    private void setByteBuffer(ByteBuffer buffer) {
        ByteBuffer oldBuffer = this.buffer;
        if (oldBuffer != null) {
            if (doNotFree) {
                doNotFree = false;
            } else {
                freeDirect(oldBuffer);
            }
        }

        this.buffer = buffer;
        tmpNioBuf = null;
        capacity = buffer.remaining();
    }

再看一下UnpooledUnsafeDirectByteBuf构造函数的逻辑。

    protected UnpooledUnsafeDirectByteBuf(ByteBufAllocator alloc, int initialCapacity, int maxCapacity) {
        super(maxCapacity);
        if (alloc == null) {
            throw new NullPointerException("alloc");
        }
        if (initialCapacity < 0) {
            throw new IllegalArgumentException("initialCapacity: " + initialCapacity);
        }
        if (maxCapacity < 0) {
            throw new IllegalArgumentException("maxCapacity: " + maxCapacity);
        }
        if (initialCapacity > maxCapacity) {
            throw new IllegalArgumentException(String.format(
                    "initialCapacity(%d) > maxCapacity(%d)", initialCapacity, maxCapacity));
        }

        this.alloc = alloc;
        setByteBuffer(allocateDirect(initialCapacity), false);
    }

    final void setByteBuffer(ByteBuffer buffer, boolean tryFree) {
        if (tryFree) {
            ByteBuffer oldBuffer = this.buffer;
            if (oldBuffer != null) {
                if (doNotFree) {
                    doNotFree = false;
                } else {
                    freeDirect(oldBuffer);
                }
            }
        }
        this.buffer = buffer;
        // 计算ByteBuffer的内存地址
        memoryAddress = PlatformDependent.directBufferAddress(buffer);
        tmpNioBuf = null;
        capacity = buffer.remaining();
    }

可以看到,方法整体逻辑与非unsafe方式是一样的,只不过在setByteBuffer方法中额外计算了ByteBuffer的内存地址。

再来看一下两个类对于_getByte方法的实现有什么不同:

UnpooledDirectByteBuf直接通过index在ByteBuffer中获取数据。

    @Override
    protected byte _getByte(int index) {
        return buffer.get(index);
    }

而UnpooledUnsafeDirectByteBuf则是通过前面保存的内存地址memoryAddress利用unsafe获取数据。

    @Override
    protected byte _getByte(int index) {
        return UnsafeByteBufUtil.getByte(addr(index));
    }

    long addr(int index) {
        return memoryAddress + index;
    }
    ...
    static byte getByte(long address) {
        return UNSAFE.getByte(address);
    }

PooledByteBufAllocator

PooledByteBufAllocator是池化分配器,首先看一下它对于newHeapBuffer方法与newDirectBuffer方法的实现逻辑。

    private final PoolThreadLocalCache threadCache;
    ...
    @Override
    protected ByteBuf newHeapBuffer(int initialCapacity, int maxCapacity) {
        // 线程局部缓存,在多线程情况下,每个线程有一份独立的缓存
        PoolThreadCache cache = threadCache.get();
        PoolArena<byte[]> heapArena = cache.heapArena;

        ByteBuf buf;
        if (heapArena != null) {
            buf = heapArena.allocate(cache, initialCapacity, maxCapacity);
        } else {
            buf = new UnpooledHeapByteBuf(this, initialCapacity, maxCapacity);
        }

        return toLeakAwareBuffer(buf);
    }

    @Override
    protected ByteBuf newDirectBuffer(int initialCapacity, int maxCapacity) {
        PoolThreadCache cache = threadCache.get();
        PoolArena<ByteBuffer> directArena = cache.directArena;

        ByteBuf buf;
        if (directArena != null) {
            buf = directArena.allocate(cache, initialCapacity, maxCapacity);
        } else {
            if (PlatformDependent.hasUnsafe()) {
                buf = UnsafeByteBufUtil.newUnsafeDirectByteBuf(this, initialCapacity, maxCapacity);
            } else {
                buf = new UnpooledDirectByteBuf(this, initialCapacity, maxCapacity);
            }
        }

        return toLeakAwareBuffer(buf);
    }

可以看到,与unpooled模式对比,pooled模式申请内存的方式不是直接去系统申请,而是先从threadCache对象获取一个线程局部缓存对象,再获取其中的PoolArena对象,由PoolArena对象来为ByteBuf分配内存。


PoolThreadLocalCache

threadCache的对象类型是PoolThreadLocalCache,它可以看做是一个增强版的ThreadLocal,这样每个线程都会有一个自己的cache,而这个cache又维护了两类内存,分别是heap(堆内)和direct(堆外)内存。


PoolArena

PoolArena可以理解为内存分配的竞技场。在PooledByteBufAllocator初始化时,会分别创建heapArenas与directArenas两个arena数组,数组大小默认是CPU核数 * 2,与NioEvenLoop的默认数量是一致的,目的是保证每个线程都有一个自己的arena,避免竞争。当有新的线程请求时,PoolThreadCache会拿到对应的PoolArena对象,保存到ThreadLocal成员变量中,这样就完成了线程与arena的绑定。


directArena分配direct内存流程

  1. 从Recycler(对象回收池)获取PooledByteBuf进行复用。
  2. 优先从之前分配过的缓存中分配内存。
  3. 如果没有命中缓存则从内存堆中分配内存。
发布了155 篇原创文章 · 获赞 106 · 访问量 20万+

猜你喜欢

转载自blog.csdn.net/u011212394/article/details/103984870
今日推荐