Class AdaptivePoolingAllocator
- All Implemented Interfaces:
AdaptiveByteBufAllocator.AdaptiveAllocatorApi
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.
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescription(package private) static final classprivate static final classprivate static final classprivate static final classprivate static class(package private) static interfaceThe strategy for howAdaptivePoolingAllocatorshould allocate chunk buffers.private static interfaceprivate static interfaceprivate static interfaceprivate static final classprivate static final classprivate static final classprivate static final classprivate static final classprivate static final classprivate static final classprivate static final classprivate static final classRemoves per-allocation retain()/release() atomic ops from the hot path by replacing ref counting with a segment-count state machine. -
Field Summary
FieldsModifier and TypeFieldDescriptionprivate static final intprivate static final intThe capacity if the chunk reuse queues, that allow chunks to be shared across magazines in a group.private final AdaptivePoolingAllocator.ChunkAllocatorprivate final AdaptivePoolingAllocator.ChunkRegistryprivate static final booleanWhether the IS_LOW_MEM setting should disable thread-local magazines.private static final intprivate static final intprivate static final booleanprivate final AdaptivePoolingAllocator.MagazineGroupprivate static final intprivate static final intThe capacity if the magazine local buffer queue.private static final intThe maximum size of a pooled chunk, in bytes.private static final intprivate static final int(package private) static final intThe 128 KiB minimum chunk size is chosen to encourage the system allocator to delegate to mmap for chunk allocations.private static final intprivate static final int[]The size classes are chosen based on the following observation:private static final intprivate static final byte[]private final AdaptivePoolingAllocator.MagazineGroup[]private final FastThreadLocal<AdaptivePoolingAllocator.MagazineGroup[]> -
Constructor Summary
ConstructorsConstructorDescriptionAdaptivePoolingAllocator(AdaptivePoolingAllocator.ChunkAllocator chunkAllocator, boolean useCacheForNonEventLoopThreads) -
Method Summary
Modifier and TypeMethodDescriptionallocate(int size, int maxCapacity) allocate(int size, int maxCapacity, Thread currentThread, AdaptivePoolingAllocator.AdaptiveByteBuf buf) allocateFallback(int size, int maxCapacity, Thread currentThread, AdaptivePoolingAllocator.AdaptiveByteBuf buf) private static AdaptivePoolingAllocator.MagazineGroup[]createMagazineGroupSizeClasses(AdaptivePoolingAllocator allocator, boolean isThreadLocal) private static Queue<AdaptivePoolingAllocator.SizeClassedChunk> Create a thread-safe multi-producer, multi-consumer queue to hold chunks that spill over from the internal Magazines.protected voidfinalize()private voidfree()getFallbackMagazine(Thread currentThread) (package private) static int[](package private) voidreallocate(int size, int maxCapacity, AdaptivePoolingAllocator.AdaptiveByteBuf into) Allocate into the given buffer.(package private) static intsizeClassIndexOf(int size) private static intsizeIndexOf(int size) long
-
Field Details
-
LOW_MEM_THRESHOLD
private static final int LOW_MEM_THRESHOLD- See Also:
-
IS_LOW_MEM
private static final boolean IS_LOW_MEM -
DISABLE_THREAD_LOCAL_MAGAZINES_ON_LOW_MEM
private static final boolean DISABLE_THREAD_LOCAL_MAGAZINES_ON_LOW_MEMWhether the IS_LOW_MEM setting should disable thread-local magazines. This can have fairly high performance overhead. -
MIN_CHUNK_SIZE
static final int MIN_CHUNK_SIZEThe 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:
-
EXPANSION_ATTEMPTS
private static final int EXPANSION_ATTEMPTS- See Also:
-
INITIAL_MAGAZINES
private static final int INITIAL_MAGAZINES- See Also:
-
RETIRE_CAPACITY
private static final int RETIRE_CAPACITY- See Also:
-
MAX_STRIPES
private static final int MAX_STRIPES -
BUFS_PER_CHUNK
private static final int BUFS_PER_CHUNK- See Also:
-
MAX_CHUNK_SIZE
private static final int MAX_CHUNK_SIZEThe maximum size of a pooled chunk, in bytes. Allocations bigger than this will never be pooled.This number is 8 MiB, and is derived from the limitations of internal histograms.
-
MAX_POOLED_BUF_SIZE
private static final int MAX_POOLED_BUF_SIZE -
CHUNK_REUSE_QUEUE
private static final int CHUNK_REUSE_QUEUEThe capacity if the chunk reuse queues, that allow chunks to be shared across magazines in a group. The default size is twiceNettyRuntime.availableProcessors(), same as the maximum number of magazines per magazine group. -
MAGAZINE_BUFFER_QUEUE_CAPACITY
private static final int MAGAZINE_BUFFER_QUEUE_CAPACITYThe 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. -
SIZE_CLASSES
private static final int[] SIZE_CLASSESThe size classes are chosen based on the following observation:Most allocations, particularly ones above 256 bytes, aim to be a power-of-2. However, many use cases, such as framing protocols, are themselves operating or moving power-of-2 sized payloads, to which they add a small amount of overhead, such as headers or checksums. This means we seem to get a lot of mileage out of having both power-of-2 sizes, and power-of-2-plus-a-bit.
On the conflicting requirements of both having as few chunks as possible, and having as little wasted memory within each chunk as possible, this seems to strike a surprisingly good balance for the use cases tested so far.
-
SIZE_CLASSES_COUNT
private static final int SIZE_CLASSES_COUNT -
SIZE_INDEXES
private static final byte[] SIZE_INDEXES -
chunkAllocator
-
chunkRegistry
-
sizeClassedMagazineGroups
-
largeBufferMagazineGroup
-
threadLocalGroup
-
-
Constructor Details
-
AdaptivePoolingAllocator
AdaptivePoolingAllocator(AdaptivePoolingAllocator.ChunkAllocator chunkAllocator, boolean useCacheForNonEventLoopThreads)
-
-
Method Details
-
createMagazineGroupSizeClasses
private static AdaptivePoolingAllocator.MagazineGroup[] createMagazineGroupSizeClasses(AdaptivePoolingAllocator allocator, boolean isThreadLocal) -
allocate
- Specified by:
allocatein interfaceAdaptiveByteBufAllocator.AdaptiveAllocatorApi
-
allocate
private AdaptivePoolingAllocator.AdaptiveByteBuf allocate(int size, int maxCapacity, Thread currentThread, AdaptivePoolingAllocator.AdaptiveByteBuf buf) -
sizeIndexOf
private static int sizeIndexOf(int size) -
sizeClassIndexOf
static int sizeClassIndexOf(int size) -
getSizeClasses
static int[] getSizeClasses() -
allocateFallback
private AdaptivePoolingAllocator.AdaptiveByteBuf allocateFallback(int size, int maxCapacity, Thread currentThread, AdaptivePoolingAllocator.AdaptiveByteBuf buf) -
getFallbackMagazine
-
reallocate
Allocate into the given buffer. Used byAdaptivePoolingAllocator.AdaptiveByteBuf.capacity(int). -
usedMemory
public long usedMemory()- Specified by:
usedMemoryin interfaceAdaptiveByteBufAllocator.AdaptiveAllocatorApi
-
finalize
-
free
private void free()
-