【Netty】ByteBuf相关(二):UnPooledByteBufAllocator创建4种类型ByteBuf

【Netty】ByteBuf相关(二):UnPooledByteBufAllocator创建4种类型ByteBuf

Scroll Down

前言

这一节就来看看UnPooledByteBufAllocator创建ByteBuf的大致流程,对比起后边学的PooledByteBufAllocator,实在是简单得不能再简单了。

另外,本节不会再阐述不同ByteBuf的区别,有兴趣的可以参考上一节,如果对本文某些名词感到陌生,也建议回去看看。

Netty Version:4.1.6


跟进源码

由于UnPooledByteBufAllocator创建ByteBuf的过程比较简单,不像PooledByteBufAllocator那般复杂,所以下面会讲到heap、direct两种内存的分配。

另外下面讲到的方法,入口其实都是如下代码,由于中间过程很简单,所以就略了:

PooledByteBufAllocator allocator = PooledByteBufAllocator.DEFAULT;
ByteBuf byteBuf = allocator.directBuffer(16);
ByteBuf byteBuf = allocator.heapBuffer(16);

分配Heap内存

UnpooledByteBufAllocator中分配heap内存是使用newHeapBuffer方法,我们找到这个方法,此处【坐标1】
io.netty.buffer.UnpooledByteBufAllocator#newHeapBuffer

    @Override
    protected ByteBuf newHeapBuffer(int initialCapacity, int maxCapacity) {
        return PlatformDependent.hasUnsafe() ? new UnpooledUnsafeHeapByteBuf(this, initialCapacity, maxCapacity)
                : new UnpooledHeapByteBuf(this, initialCapacity, maxCapacity);
    }
  • initialCapacity就是初始申请的内存大小,也就是用户需要的内存大小。
  • maxCapacity是指内存最大允许大小,默认传进来是Integer.MAX_VALUE。
  • 如果Netty能拿到jdk底层的unsafe,就会调用UnpooledUnsafeHeapByteBuf构造。

假设现在Netty能拿不到jdk底层的unsafe对象,那就是调用UnpooledHeapByteBuf的构造方法了,跟进去瞧瞧:
io.netty.buffer.UnpooledHeapByteBuf#UnpooledHeapByteBuf(io.netty.buffer.ByteBufAllocator, int, int)

    protected UnpooledHeapByteBuf(ByteBufAllocator alloc, int initialCapacity, int maxCapacity) {
        this(alloc, new byte[initialCapacity], 0, 0, maxCapacity);
    }
  • 到这一步其实就不用再胡乱猜想了,就是通过new byte数据来创建、分配内存给ByteBuf。

继续跟进this:
io.netty.buffer.UnpooledHeapByteBuf#UnpooledHeapByteBuf(io.netty.buffer.ByteBufAllocator, byte[], int, int, int)

    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;
    }
  • 其实就是保存属性,还有初始化指针偏移量。
  • 之后所有的内存操作都是基于上面初始化的byte数组和指针,充分体现了heap的特性。


好了,返回最初的neHeapBuffer方法【坐标1】,看看UnpooledUnsafeHeapByteBuf构造方法:
io.netty.buffer.UnpooledUnsafeHeapByteBuf#UnpooledUnsafeHeapByteBuf

    UnpooledUnsafeHeapByteBuf(ByteBufAllocator alloc, int initialCapacity, int maxCapacity) {
        super(alloc, initialCapacity, maxCapacity);
    }

跟进super方法:
io.netty.buffer.UnpooledHeapByteBuf#UnpooledHeapByteBuf(io.netty.buffer.ByteBufAllocator, int, int)

    protected UnpooledHeapByteBuf(ByteBufAllocator alloc, int initialCapacity, int maxCapacity) {
        this(alloc, new byte[initialCapacity], 0, 0, maxCapacity);
    }
  • 发现UnpooledUnsafeHeapByteBuf的构造方法其实就是原封不动的调用UnpooledHeapByteBuf的构造方法。
  • 那既然它们两的构造方法都一样,那它们到底有什么不同呢?其实这个在上一节讲是否unsafe区别的时候就当道了,主要是在内存上读写时是否用底层的native方法。

分配Direct内存

UnpooledByteBufAllocator分配direct内存最终是newDirectBuffer方法创建,下面找到这个方法,此处【坐标2】
io.netty.buffer.UnpooledByteBufAllocator#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);
    }
  • 同样由Netty是否拿到jdk底层unsafe对象,决定是否创建unsafe类型的。

这里先跟进非unsafe的构造,即跟进UnpooledDirectByteBuf构造方法:
io.netty.buffer.UnpooledDirectByteBuf#UnpooledDirectByteBuf(io.netty.buffer.ByteBufAllocator, int, int)

    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));
    }
  • 重点关注最后一行,前面的都是校验、属性保存。

跟进allocateDirect方法:
java.nio.ByteBuffer#allocateDirect

    public static ByteBuffer allocateDirect(int capacity) {
        return new DirectByteBuffer(capacity);
    }
  • 可见到这一步就是创建jdk底层的DirectByteBuffer对象,而DirectByteBuffer的构建则是基于jdk的unsafe分配直接内存的。

返回,最终setByteBuffer方法保存DirectByteBuffer对象:
io.netty.buffer.UnpooledDirectByteBuf#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();
    }

然后ByteBuf构造完成,返回给用户。


返回到【坐标2】的newDirectBuffer方法,跟进UnsafeByteBufUtil.newUnsafeDirectByteBuf方法:
io.netty.buffer.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);
    }
  • 关于UnpooledUnsafeNoCleanerDirectByteBuf,其实就是继承了UnpooledUnsafeDirectByteBuf并腹泻了一些方法,有兴趣的可以自己看看,这里不再赘述。
  • 我们应该关注UnpooledUnsafeDirectByteBuf的构造。

跟进UnpooledUnsafeDirectByteBuf构造方法:
io.netty.buffer.UnpooledUnsafeDirectByteBuf#UnpooledUnsafeDirectByteBuf(io.netty.buffer.ByteBufAllocator, int, int)

    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);
    }
  • 这里的setByteBuffer和上面非unsafe的同名方法是有点不一样的的,后面会继续跟进。

跟进allocateDirect方法,最终还是通过构造jdk底层的DirectByteBuffer获取到内存:
java.nio.ByteBuffer#allocateDirect

    public static ByteBuffer allocateDirect(int capacity) {
        return new DirectByteBuffer(capacity);
    }

返回,跟进setByteBuffer方法:
io.netty.buffer.UnpooledUnsafeDirectByteBuf#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();
    }

可以发现与非unsafe对比,unsafe最终是存储了DirectByteBuffer对象,并计算出了内存地址memoryAddress,便于Netty使用jdk底层unsafe直接操作内存。这里的memoryAddress是个很重要的参数,之后此ByteBuf做读写时,memoryAddress都会参与到某些位运算定位内存的真实地址。


至此,UnPooledByteBufAllocator创建4种类新ByteBuf的流程就结束了。


小结

  • UnPooledByteBufAllocator创建不同类型的ByteBuf,会有不同的策略。底层存储也不一样,比如heap内存实质上就是byte数组,而direct内存则是jdk底层的DirectByteBuffer对象。