Class AdaptivePoolingAllocator.ThreadLocalSizeClassedChunkCache
- All Implemented Interfaces:
AdaptivePoolingAllocator.ChunkCache
- Enclosing class:
AdaptivePoolingAllocator
Logical layout after purge:
head tail
v v
[..., notEmpty, notEmpty, ..., empty, empty, ..., null, ...]
|--- notEmptyCount ---|--- emptyCount --|
|------------ count ------------------|
Physical layout when the ring wraps:
0 tail head length
v v v v
[...tail] [ unused ] [head................]
^ |--- content wraps ---|
wrap point
scanForCapacity — O(1) fast path takes from head while notEmptyCount > 0:
before: notEmptyCount=2, count=5
[NE, NE, E, E, E, _, _, _]
^head ^tail
after: returns NE, notEmptyCount=1, count=4
[_, NE, E, E, E, _, _, _]
^head ^tail
Fallback when notEmptyCount == 0: linear scan of the empty zone for chunks
that gained capacity from external segment returns.
offerChunk — write at tail, grow (double + linearize) if full:
before: count=4
[_, NE, E, E, E, _, _, _]
^head ^tail
after: count=5
[_, NE, E, E, E, X, _, _]
^head ^tail
runPurgeScan (every AdaptivePoolingAllocator.CHUNK_PURGE_POLLS_THREAD_LOCAL polls) —
two passes. Pass 1: age idle chunks (full → epoch++, non-full → epoch=0), evict
past threshold, compact survivors (nulls stale slots inline). Pass 2: partition
hasCap to front / noCap to back, then three-way Dutch-flag within hasCap into
[epoch=0 | 0<epoch<threshold | epoch>=threshold]. Chunks with epoch>=threshold
are placed at the back of hasCap so scan doesn't reach them — they age to
threshold+1 and get evicted. Never selects — selection is always
scanForCapacity.
Case 1 — no eviction, an empty chunk gained capacity externally (common):
before (E* gained capacity since last purge):
[NE, NE, E*, E, _, _, _, _]
^head ^tail
notEmptyCount=2
pass 1: age idle chunks. None past threshold. No compaction needed.
pass 2 (partition): E* now has capacity → placed in notEmpty zone.
after:
[NE, NE, E*, E, _, _, _, _]
^head ^tail
notEmptyCount=3
Case 2 — eviction (uncommon, burst wind-down):
before (ring wraps, IDLE* = idle past threshold):
[E, NE, _, IDLE*, NE, E, E, NE]
^tail ^head
pass 1: IDLE* evicted (markToDeallocate), survivors compacted, stale slots nulled.
[_, _, _, NE, E, E, NE, E]
^tail ^head
|--- kept=6 ---|
pass 2 (partition): [epoch=0 hasCap | 0<epoch<T hasCap | epoch>=T hasCap | noCap].
[_, _, _, NE, NE, E, E, E]
^tail ^head
notEmptyCount=2, count=6
Idle chunks (remainingCapacity == capacity) age via purgeEpoch and are evicted
past threshold, but at least purgeRetentionFloor chunks are always retained.-
Field Summary
FieldsModifier and TypeFieldDescription(package private) AdaptivePoolingAllocator.SizeClassedChunk[](package private) int(package private) int(package private) final int(package private) intprivate long(package private) final int(package private) int -
Constructor Summary
Constructors -
Method Summary
Methods inherited from class io.netty.buffer.AdaptivePoolingAllocator.SizeClassedChunkCache
create
-
Field Details
-
chunks
-
head
int head -
tail
int tail -
count
int count -
notEmptyCount
int notEmptyCount -
purgeBudget
private long purgeBudget -
maxCachedChunks
final int maxCachedChunks -
purgeRetentionFloor
final int purgeRetentionFloor
-
-
Constructor Details
-
ThreadLocalSizeClassedChunkCache
ThreadLocalSizeClassedChunkCache(int chunkSize)
-
-
Method Details
-
forcePurge
AdaptivePoolingAllocator.SizeClassedChunk forcePurge()- Specified by:
forcePurgein classAdaptivePoolingAllocator.SizeClassedChunkCache
-
pollChunk
- Specified by:
pollChunkin interfaceAdaptivePoolingAllocator.ChunkCache- Specified by:
pollChunkin classAdaptivePoolingAllocator.SizeClassedChunkCache
-
scanForCapacity
-
scanForCapacityFallback
-
runPurgeScan
private void runPurgeScan() -
partition
private void partition(int size) -
offerChunk
-
toString
-
free
public void free() -
isEmpty
public boolean isEmpty()
-