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.

    • Method Detail

      • createSharedChunkQueue

        private static java.util.Queue<AdaptivePoolingAllocator.Chunk> createSharedChunkQueue()
        Create a thread-safe multi-producer, multi-consumer queue to hold chunks that spill over from the internal Magazines.

        Each Magazine can only hold two chunks at any one time: the chunk it currently allocates from, and the next-in-line chunk which will be used for allocation once the current one has been used up. This queue will be used by magazines to share any excess chunks they allocate, so that they don't need to allocate new chunks when their current and next-in-line chunks have both been used up.

        The simplest implementation of this method is to return a new ConcurrentLinkedQueue. However, the CLQ is unbounded, and this means there's no limit to how many chunks can be cached in this queue.

        Each chunk in this queue can be up to MAX_CHUNK_SIZE in size, so it is recommended to use a bounded queue to limit the maximum memory usage.

        The default implementation will create a bounded queue with a capacity of CENTRAL_QUEUE_CAPACITY.

        Returns:
        A new multi-producer, multi-consumer queue.
      • tryExpandMagazines

        private boolean tryExpandMagazines​(int currentLength)
      • finalize

        protected void finalize()
                         throws java.lang.Throwable
        Overrides:
        finalize in class java.lang.Object
        Throws:
        java.lang.Throwable
      • free

        private void free()
      • sizeBucket

        static int sizeBucket​(int size)