Class AdaptivePoolingAllocator

  • All Implemented Interfaces:
    AdaptiveByteBufAllocator.AdaptiveAllocatorApi

    @UnstableApi
    final class AdaptivePoolingAllocator
    extends java.lang.Object
    implements AdaptiveByteBufAllocator.AdaptiveAllocatorApi
    An auto-tuning pooling allocator, that follows an anti-generational hypothesis.

    The allocator is organized into a list of Magazines, and each magazine has a chunk-buffer that they allocate buffers from.

    The magazines hold the mutexes that ensure the thread-safety of the allocator, and each thread picks a magazine based on the id of the thread. This spreads the contention of multi-threaded access across the magazines. If contention is detected above a certain threshold, the number of magazines are increased in response to the contention.

    The magazines maintain histograms of the sizes of the allocations they do. The histograms are used to compute the preferred chunk size. The preferred chunk size is one that is big enough to service 10 allocations of the 99-percentile size. This way, the chunk size is adapted to the allocation patterns.

    Computing the preferred chunk size is a somewhat expensive operation. Therefore, the frequency with which this is done, is also adapted to the allocation pattern. If a newly computed preferred chunk is the same as the previous preferred chunk size, then the frequency is reduced. Otherwise, the frequency is increased.

    This allows the allocator to quickly respond to changes in the application workload, without suffering undue overhead from maintaining its statistics.

    Since magazines are "relatively thread-local", the allocator has a central queue that allow excess chunks from any magazine, to be shared with other magazines. The createSharedChunkQueue() method can be overridden to customize this queue.

    • Field Detail

      • MIN_CHUNK_SIZE

        private static final int MIN_CHUNK_SIZE
        The 128 KiB minimum chunk size is chosen to encourage the system allocator to delegate to mmap for chunk allocations. For instance, glibc will do this. This pushes any fragmentation from chunk size deviations off physical memory, onto virtual memory, which is a much, much larger space. Chunks are also allocated in whole multiples of the minimum chunk size, which itself is a whole multiple of popular page sizes like 4 KiB, 16 KiB, and 64 KiB.
        See Also:
        Constant Field Values
      • MAX_STRIPES

        private static final int MAX_STRIPES
      • MAX_CHUNK_SIZE

        private static final int MAX_CHUNK_SIZE
        The maximum size of a pooled chunk, in bytes. Allocations bigger than this will never be pooled.

        This number is 10 MiB, and is derived from the limitations of internal histograms.

        See Also:
        Constant Field Values
      • CENTRAL_QUEUE_CAPACITY

        private static final int CENTRAL_QUEUE_CAPACITY
        The capacity if the central queue that allow chunks to be shared across magazines. The default size is NettyRuntime.availableProcessors(), and the maximum number of magazines is twice this.

        This means the maximum amount of memory that we can have allocated-but-not-in-use is 5 * NettyRuntime.availableProcessors() * MAX_CHUNK_SIZE bytes.

      • MAGAZINE_BUFFER_QUEUE_CAPACITY

        private static final int MAGAZINE_BUFFER_QUEUE_CAPACITY
        The capacity if the magazine local buffer queue. This queue just pools the outer ByteBuf instance and not the actual memory and so helps to reduce GC pressure.
      • NO_MAGAZINE

        private static final java.lang.Object NO_MAGAZINE
      • magazineExpandLock

        private final java.util.concurrent.locks.StampedLock magazineExpandLock
      • threadLocalMagazine

        private final FastThreadLocal<java.lang.Object> threadLocalMagazine
      • freed

        private volatile boolean freed