NOTE

ByteBufAllocator

ByteBufAllocator class hierarchy, AbstractByteBufAllocator, unpooled allocation, and pooled allocation

JavaCreated Updated 1 min readhistorical

This is a historical learning note and may contain outdated or incomplete understanding.

ByteBufAllocator Class Hierarchy

A utility class for allocating memory.

ByteBufAllocator

The APIs here can only distinguish between heap and direct memory. The other two dimensions are implemented by the buffer method of AbstractByteBufAllocator.

buffer// used to allocate a buffer

ioBuffer// used to allocate a buffer suitable for I/O

heapBuffer// used to allocate a buffer on the heap

directBuffer// used to allocate a direct-memory buffer

// combine multiple buffers
compositeBuffer
compositeHeapBuffer
compositeDirectBuffer

AbstractByteBufAllocator

buffer Method

Look at its buffer method:

public ByteBuf buffer() {
    // Allocate direct memory.
    if (directByDefault) {
        return directBuffer();
    }
    // Allocate heap memory.
    return heapBuffer();
}

Allocate Direct Memory

public ByteBuf directBuffer() {
    // Initial capacity and maximum capacity.
    return directBuffer(DEFAULT_INITIAL_CAPACITY, DEFAULT_MAX_CAPACITY);
}

public ByteBuf directBuffer(int initialCapacity, int maxCapacity) {
    if (initialCapacity == 0 && maxCapacity == 0) {
        return emptyBuf;
    }
    validate(initialCapacity, maxCapacity);
    // Call the subclass method; it can be pooled or unpooled.
    return newDirectBuffer(initialCapacity, maxCapacity);
}

Allocate Heap Memory

public ByteBuf heapBuffer() {
    return heapBuffer(DEFAULT_INITIAL_CAPACITY, DEFAULT_MAX_CAPACITY);
}

public ByteBuf heapBuffer(int initialCapacity, int maxCapacity) {
    if (initialCapacity == 0 && maxCapacity == 0) {
        return emptyBuf;
    }
    validate(initialCapacity, maxCapacity);
    // Call the subclass method; it can be pooled or unpooled.
    return newHeapBuffer(initialCapacity, maxCapacity);
}

UnpooledByteBufAllocator

newHeapBuffer

protected ByteBuf newHeapBuffer(int initialCapacity, int maxCapacity) {
    // Decide whether to use Unsafe to allocate memory according to whether Unsafe is available.
    return PlatformDependent.hasUnsafe() ?
            new InstrumentedUnpooledUnsafeHeapByteBuf(this, initialCapacity, maxCapacity) :
            new InstrumentedUnpooledHeapByteBuf(this, initialCapacity, maxCapacity);
}

First look at InstrumentedUnpooledUnsafeHeapByteBuf, the version that uses Unsafe.

InstrumentedUnpooledUnsafeHeapByteBuf(UnpooledByteBufAllocator alloc, int initialCapacity, int maxCapacity) {
    // UnpooledUnsafeHeapByteBuf
    super(alloc, initialCapacity, maxCapacity);
}

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

public UnpooledHeapByteBuf(ByteBufAllocator alloc, int initialCapacity, int maxCapacity) {
    super(maxCapacity);

    checkNotNull(alloc, "alloc");

    if (initialCapacity > maxCapacity) {
        throw new IllegalArgumentException(String.format(
                "initialCapacity(%d) > maxCapacity(%d)", initialCapacity, maxCapacity));
    }

    this.alloc = alloc;
    // On the heap this is new byte[initialCapacity].
    setArray(allocateArray(initialCapacity));
    setIndex(0, 0);
}

newDirectBuffer

protected ByteBuf newDirectBuffer(int initialCapacity, int maxCapacity) {
    final ByteBuf buf;
    // Decide whether to use Unsafe to allocate memory according to whether Unsafe is available.
    if (PlatformDependent.hasUnsafe()) {
        buf = noCleaner ? new InstrumentedUnpooledUnsafeNoCleanerDirectByteBuf(this, initialCapacity, maxCapacity) :
                new InstrumentedUnpooledUnsafeDirectByteBuf(this, initialCapacity, maxCapacity);
    } else {
        buf = new InstrumentedUnpooledDirectByteBuf(this, initialCapacity, maxCapacity);
    }
    return disableLeakDetector ? buf : toLeakAwareBuffer(buf);
}

With Unsafe

InstrumentedUnpooledUnsafeNoCleanerDirectByteBuf(
        UnpooledByteBufAllocator alloc, int initialCapacity, int maxCapacity) {
    // UnpooledUnsafeNoCleanerDirectByteBuf
    super(alloc, initialCapacity, maxCapacity);
}

public 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;
    // direct uses ByteBuffer.allocateDirect(initialCapacity)
    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;
    // This is an important point. It ultimately uses UNSAFE.getLong(object, fieldOffset).
    memoryAddress = PlatformDependent.directBufferAddress(buffer);
    tmpNioBuf = null;
    capacity = buffer.remaining();
}

Without Unsafe

InstrumentedUnpooledDirectByteBuf(
        UnpooledByteBufAllocator alloc, int initialCapacity, int maxCapacity) {
    super(alloc, initialCapacity, maxCapacity);
}

public 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;
    // new DirectByteBuffer(capacity)
    setByteBuffer(ByteBuffer.allocateDirect(initialCapacity));
}

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

    // Without Unsafe it simply stores the buffer.
    this.buffer = buffer;
    tmpNioBuf = null;
    capacity = buffer.remaining();
}

PooledByteBufAllocator

PooledByteBufAllocator

Discussion

Sign in with GitHub to comment. Discussions are stored as GitHub Issues.View on GitHub