Netty学习——源码篇11 Netty非池化内存分配 备份

admin2024-04-03  0

1 堆内内存的分配

        现在来看UnpooledByteBufAllocator的内存分配原理。首先是heapBuffer的分配逻辑,newHeapBuffer方法的代码如下:

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

        通过调用PlatformDependent.hasUnsafe()方法来判断操作系统是否支持Unsafe,如果支持Unsafe则创建UnpooledUnsafeHeapByteBuf类,否则创建UnpooledHeapByteBuf类。先看一下UnpooledUnsafeHeapByteBuf的构造器会进行哪些操作,代码如下:

final class UnpooledUnsafeHeapByteBuf extends UnpooledHeapByteBuf {

    /**
     * Creates a new heap buffer with a newly allocated byte array.
     *
     * @param initialCapacity the initial capacity of the underlying byte array
     * @param maxCapacity the max capacity of the underlying byte array
     */
    UnpooledUnsafeHeapByteBuf(ByteBufAllocator alloc, int initialCapacity, int maxCapacity) {
        super(alloc, initialCapacity, maxCapacity);
    }
}

        发现UnpooledUnsafeHeapByteBuf继承了UnpooledHeapByte,并且在UnpooledUnsafeHeapByteBuf的构造器中直接调用了super方法,也就是其父类UnpooledHeapByte的构造方法。看一下UnpooledHeapByte的构造方法。

public class UnpooledHeapByteBuf extends AbstractReferenceCountedByteBuf {

    private final ByteBufAllocator alloc;
    byte[] array;
    private ByteBuffer tmpNioBuf;

    /**
     * Creates a new heap buffer with a newly allocated byte array.
     *
     * @param initialCapacity the initial capacity of the underlying byte array
     * @param maxCapacity the max capacity of the underlying byte array
     */
    protected UnpooledHeapByteBuf(ByteBufAllocator alloc, int initialCapacity, int maxCapacity) {
        this(alloc, new byte[initialCapacity], 0, 0, maxCapacity);
    }

    protected UnpooledHeapByteBuf(ByteBufAllocator alloc, byte[] initialArray, int maxCapacity) {
        this(alloc, initialArray, 0, initialArray.length, 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);
    }

}

        其中调用了一个关键方法就是setArray()方法。这个方法的功能非常简单,就是把默认分配的数组new byte[initialCapacity]赋值给全局变量initialArray数组。

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

        紧接着调用setIndex方法。

    @Override
    public ByteBuf setIndex(int readerIndex, int writerIndex) {
        if (readerIndex < 0 || readerIndex > writerIndex || writerIndex > capacity()) {
            throw new IndexOutOfBoundsException(String.format(
                    "readerIndex: %d, writerIndex: %d (expected: 0 <= readerIndex <= writerIndex <= capacity(%d))",
                    readerIndex, writerIndex, capacity()));
        }
        setIndex0(readerIndex, writerIndex);
        return this;
    }
    final void setIndex0(int readerIndex, int writerIndex) {
        this.readerIndex = readerIndex;
        this.writerIndex = writerIndex;
    }

        最终setIndex0()方法中初始化readerIndex属性和writerIndex属性。

        既然UnpooledUnsafeHeapByteBuf和UnpooledHeapByteBuf调用的都是UnpooledHeapByteBuf的构造方法,那么他们之间到底有什么区别呢?根本区别在于I/O的读写,分别来看它们的getByte()方法,了解二者的区别。

        先看UnpooledHeapByteBuf的getByte()方法的实现代码。

    @Override
    public byte getByte(int index) {
        ensureAccessible();
        return _getByte(index);
    }
    @Override
    protected byte _getByte(int index) {
        return HeapByteBufUtil.getByte(array, index);
    }

        可以看到,最终调用的是HeapByteBufUtil.getByte(array, index)方法。

    static byte getByte(byte[] memory, int index) {
        return memory[index];
    }

        getByte()这个方法中的处理逻辑非常简单,就是根据index索引直接从数组中取值。接着来看UnpooledUnsafeHeapByteBuf的getByte()方法。

    @Override
    public byte getByte(int index) {
        checkIndex(index);
        return _getByte(index);
    }

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

        可以看到,最终调用的是UnsafeByteBufUtil.getByte(array, index)的方法。通过这样对比代码,已经基本了解UnpooledUnsafeHeapByteBuf和UnpooledHeapByteBuf的区别了。

2 堆外内存的分配

        再回到UnpooledHeapByteBuf的newDirectBuffer方法,代码如下:

    @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);
    }

    这段代码可以看出,如果支持Unsafe则调用UnsafeByteBufUtil.newUnsafeDirectByteBuf,否则创建UnpooledDirectByteBuf对象。看一下UnpooledDirectByteBuf构造器。

public class UnpooledDirectByteBuf extends AbstractReferenceCountedByteBuf {

    private final ByteBufAllocator alloc;

    private ByteBuffer buffer;
    private ByteBuffer tmpNioBuf;
    private int capacity;
    private boolean doNotFree;

    /**
     * Creates a new direct buffer.
     *
     * @param initialCapacity the initial capacity of the underlying direct buffer
     * @param maxCapacity     the maximum capacity of the underlying direct buffer
     */
    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;
        setByteBuffer(ByteBuffer.allocateDirect(initialCapacity));
    }
}

        首先调用ByteBuffer.allocateDirect.allocatoDirect()通过JDK底层分配了一个直接缓冲区,然后传给setByteBuffer方法。继续跟进,代码如下。

    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();
    }

         上面代码可以看到,setByteBuffer方法主要做了一次赋值。继续看UnsafeByteBufUtil.newUnsafeDirectByteBuf()方法的逻辑。

static UnpooledUnsafeDirectByteBuf newUnsafeDirectByteBuf(
            ByteBufAllocator alloc, int initialCapacity, int maxCapacity) {
        if (PlatformDependent.useDirectBufferNoCleaner()) {
            return new UnpooledUnsafeNoCleanerDirectByteBuf(alloc, initialCapacity, maxCapacity);
        }
        return new UnpooledUnsafeDirectByteBuf(alloc, initialCapacity, maxCapacity);
    }

        这个方法返回了一个UnpooledUnsafeDirectByteBuf对象。下面继续来看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);
    }

          UnpooledUnsafeDirectByteBuf构造器的逻辑和UnpooledDirectByteBuf构造器的逻辑是相似的,其setByteBuffer方法的实现代码如下:


    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;
        memoryAddress = PlatformDependent.directBufferAddress(buffer);
        tmpNioBuf = null;
        capacity = buffer.remaining();
    }

        同样还是先保存在JDK底层创建的Buffer,接下来有个很重要的操作就是调用PlatformDependent.directBufferAddress(buffer)方法获取Buffer真是的内存地址,并保存到memoryAddress变量中。PlatformDependent.directBufferAddress(buffer)的实现代码如下:

    public static long directBufferAddress(ByteBuffer buffer) {
        return PlatformDependent0.directBufferAddress(buffer);
    }
    static long directBufferAddress(ByteBuffer buffer) {
        return getLong(buffer, ADDRESS_FIELD_OFFSET);
    }
    private static long getLong(Object object, long fieldOffset) {
        return UNSAFE.getLong(object, fieldOffset);
    }

         上述代码最终调用了Unsafe的getLong方法,这是一个native方法。直接通过Buffer的内存地址加上一个偏移量去获取数据。到这里,已经基本清楚了UnpooledUnsafeDirectByteBuf和UnpooledDirectByteBuf的区别,非Unsafe通过数组的下标取数据,Unsafe直接操作内存地址,相对于非Unsafe来说,效率当然更高。

                  

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明原文出处。如若内容造成侵权/违法违规/事实不符,请联系SD编程学习网:675289112@qq.com进行投诉反馈,一经查实,立即删除!