Class AdaptivePoolingAllocator
- java.lang.Object
-
- io.netty.buffer.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.
-
-
Nested Class Summary
-
Field Summary
Fields Modifier and Type Field Description private static intBUFS_PER_CHUNKprivate static intCHUNK_REUSE_QUEUEThe capacity if the chunk reuse queues, that allow chunks to be shared across magazines in a group.private AdaptivePoolingAllocator.ChunkAllocatorchunkAllocatorprivate AdaptivePoolingAllocator.ChunkRegistrychunkRegistryprivate static booleanDISABLE_THREAD_LOCAL_MAGAZINES_ON_LOW_MEMWhether the IS_LOW_MEM setting should disable thread-local magazines.private static intEXPANSION_ATTEMPTSprivate static intINITIAL_MAGAZINESprivate static booleanIS_LOW_MEMprivate AdaptivePoolingAllocator.MagazineGrouplargeBufferMagazineGroupprivate static intLOW_MEM_THRESHOLDprivate static intMAGAZINE_BUFFER_QUEUE_CAPACITYThe capacity if the magazine local buffer queue.private static intMAX_CHUNK_SIZEThe maximum size of a pooled chunk, in bytes.private static intMAX_POOLED_BUF_SIZEprivate static intMAX_STRIPES(package private) static intMIN_CHUNK_SIZEThe 128 KiB minimum chunk size is chosen to encourage the system allocator to delegate to mmap for chunk allocations.private static intRETIRE_CAPACITYprivate static int[]SIZE_CLASSESThe size classes are chosen based on the following observation:private static intSIZE_CLASSES_COUNTprivate static byte[]SIZE_INDEXESprivate AdaptivePoolingAllocator.MagazineGroup[]sizeClassedMagazineGroupsprivate FastThreadLocal<AdaptivePoolingAllocator.MagazineGroup[]>threadLocalGroup
-
Constructor Summary
Constructors Constructor Description AdaptivePoolingAllocator(AdaptivePoolingAllocator.ChunkAllocator chunkAllocator, boolean useCacheForNonEventLoopThreads)
-
Method Summary
All Methods Static Methods Instance Methods Concrete Methods Modifier and Type Method Description ByteBufallocate(int size, int maxCapacity)private AdaptivePoolingAllocator.AdaptiveByteBufallocate(int size, int maxCapacity, java.lang.Thread currentThread, AdaptivePoolingAllocator.AdaptiveByteBuf buf)private AdaptivePoolingAllocator.AdaptiveByteBufallocateFallback(int size, int maxCapacity, java.lang.Thread currentThread, AdaptivePoolingAllocator.AdaptiveByteBuf buf)private static AdaptivePoolingAllocator.MagazineGroup[]createMagazineGroupSizeClasses(AdaptivePoolingAllocator allocator, boolean isThreadLocal)private static java.util.Queue<AdaptivePoolingAllocator.SizeClassedChunk>createSharedChunkQueue()Create a thread-safe multi-producer, multi-consumer queue to hold chunks that spill over from the internal Magazines.protected voidfinalize()private voidfree()private AdaptivePoolingAllocator.MagazinegetFallbackMagazine(java.lang.Thread currentThread)(package private) static int[]getSizeClasses()(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)longusedMemory()
-
-
-
Field Detail
-
LOW_MEM_THRESHOLD
private static final int LOW_MEM_THRESHOLD
- See Also:
- Constant Field Values
-
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_MEM
Whether 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_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
-
EXPANSION_ATTEMPTS
private static final int EXPANSION_ATTEMPTS
- See Also:
- Constant Field Values
-
INITIAL_MAGAZINES
private static final int INITIAL_MAGAZINES
- See Also:
- Constant Field Values
-
RETIRE_CAPACITY
private static final int RETIRE_CAPACITY
- See Also:
- Constant Field Values
-
MAX_STRIPES
private static final int MAX_STRIPES
-
BUFS_PER_CHUNK
private static final int BUFS_PER_CHUNK
- See Also:
- Constant Field Values
-
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 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_QUEUE
The 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_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.
-
SIZE_CLASSES
private static final int[] SIZE_CLASSES
The 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
private final AdaptivePoolingAllocator.ChunkAllocator chunkAllocator
-
chunkRegistry
private final AdaptivePoolingAllocator.ChunkRegistry chunkRegistry
-
sizeClassedMagazineGroups
private final AdaptivePoolingAllocator.MagazineGroup[] sizeClassedMagazineGroups
-
largeBufferMagazineGroup
private final AdaptivePoolingAllocator.MagazineGroup largeBufferMagazineGroup
-
threadLocalGroup
private final FastThreadLocal<AdaptivePoolingAllocator.MagazineGroup[]> threadLocalGroup
-
-
Constructor Detail
-
AdaptivePoolingAllocator
AdaptivePoolingAllocator(AdaptivePoolingAllocator.ChunkAllocator chunkAllocator, boolean useCacheForNonEventLoopThreads)
-
-
Method Detail
-
createMagazineGroupSizeClasses
private static AdaptivePoolingAllocator.MagazineGroup[] createMagazineGroupSizeClasses(AdaptivePoolingAllocator allocator, boolean isThreadLocal)
-
createSharedChunkQueue
private static java.util.Queue<AdaptivePoolingAllocator.SizeClassedChunk> 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, theCLQis 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_SIZEin 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
CHUNK_REUSE_QUEUE.- Returns:
- A new multi-producer, multi-consumer queue.
-
allocate
public ByteBuf allocate(int size, int maxCapacity)
- Specified by:
allocatein interfaceAdaptiveByteBufAllocator.AdaptiveAllocatorApi
-
allocate
private AdaptivePoolingAllocator.AdaptiveByteBuf allocate(int size, int maxCapacity, java.lang.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, java.lang.Thread currentThread, AdaptivePoolingAllocator.AdaptiveByteBuf buf)
-
getFallbackMagazine
private AdaptivePoolingAllocator.Magazine getFallbackMagazine(java.lang.Thread currentThread)
-
reallocate
void reallocate(int size, int maxCapacity, AdaptivePoolingAllocator.AdaptiveByteBuf into)Allocate into the given buffer. Used byAdaptivePoolingAllocator.AdaptiveByteBuf.capacity(int).
-
usedMemory
public long usedMemory()
- Specified by:
usedMemoryin interfaceAdaptiveByteBufAllocator.AdaptiveAllocatorApi
-
finalize
protected void finalize() throws java.lang.Throwable- Overrides:
finalizein classjava.lang.Object- Throws:
java.lang.Throwable
-
free
private void free()
-
-