Understanding Netty's Pooled Memory Allocation Mechanism Based on jemalloc4
This article explains Netty's memory pool architecture after switching from jemalloc3 to jemalloc4, detailing the new memory size classes, core components such as PoolArena, PoolChunkList, PoolChunk, and PoolSubpage, and provides Java code snippets to illustrate their implementations.
In this article, the author revisits Netty's pooled memory allocation mechanism, focusing on the transition from jemalloc3 to jemalloc4 (version 4.1.77.Final) and explains the updated memory classification and component architecture.
Memory Classification : Under jemalloc3 Netty divided memory into Tiny (0‑512 B), Small (512 B‑8 KB), Normal (8 KB‑16 MB) and Huge (>16 MB). With jemalloc4 the Tiny class is removed, leaving Small [0‑28 KB], Normal (28 KB‑16 MB] and Huge (>16 MB).
Overall Architecture : The article shows the memory pool diagram for jemalloc3 and compares it with the jemalloc4 structure, highlighting the core components: PoolArena , PoolChunkList , PoolChunk , PoolSubpage and PoolThreadCache .
PoolArena : Serves as the entry point for external memory allocation. Netty creates a fixed number of arenas (default CPU cores × 2) configurable via io.netty.allocator.numHeapArenas . Threads select an arena using a round‑robin strategy and keep the same arena for their lifetime. The article includes the Java code that computes default arena numbers and shows the abstract class definition of PoolArena<T> with its internal structures.
final int defaultMinNumArena = NettyRuntime.availableProcessors() * 2;
final int defaultChunkSize = DEFAULT_PAGE_SIZE << DEFAULT_MAX_ORDER;
DEFAULT_NUM_HEAP_ARENA = Math.max(0,
SystemPropertyUtil.getInt(
"io.netty.allocator.numHeapArenas",
(int) Math.min(
defaultMinNumArena,
runtime.maxMemory() / defaultChunkSize / 2 / 3)));
DEFAULT_NUM_DIRECT_ARENA = Math.max(0,
SystemPropertyUtil.getInt(
"io.netty.allocator.numDirectArenas",
(int) Math.min(
defaultMinNumArena,
PlatformDependent.maxDirectMemory() / defaultChunkSize / 2 / 3)));PoolChunkList : Manages groups of PoolChunk objects with similar usage rates, forming a doubly‑linked list. Each list has minUsage and maxUsage thresholds; chunks move between lists as their usage changes. The class definition is provided.
final class PoolChunkList<T> implements PoolChunkListMetric {
private final PoolArena<T> arena;
private final PoolChunkList<T> nextList;
private final int minUsage;
private final int maxUsage;
private final int maxCapacity;
private PoolChunk<T> head;
private final int freeMinThreshold;
private final int freeMaxThreshold;
// ...
}PoolChunk : Represents the actual memory block allocated from the JVM. It contains fields for size, bitmap management, run handling, and cached NIO buffers. The article lists its full class skeleton.
final class PoolChunk<T> implements PoolChunkMetric {
private static final int SIZE_BIT_LENGTH = 15;
private static final int INUSED_BIT_LENGTH = 1;
private static final int SUBPAGE_BIT_LENGTH = 1;
private static final int BITMAP_IDX_BIT_LENGTH = 32;
static final int IS_SUBPAGE_SHIFT = BITMAP_IDX_BIT_LENGTH;
static final int IS_USED_SHIFT = SUBPAGE_BIT_LENGTH + IS_SUBPAGE_SHIFT;
static final int SIZE_SHIFT = INUSED_BIT_LENGTH + IS_USED_SHIFT;
static final int RUN_OFFSET_SHIFT = SIZE_BIT_LENGTH + SIZE_SHIFT;
final PoolArena<T> arena;
final Object base;
final T memory;
final boolean unpooled;
private final LongLongHashMap runsAvailMap;
private final LongPriorityQueue[] runsAvail;
private final PoolSubpage<T>[] subpages;
private final LongCounter pinnedBytes = PlatformDependent.newLongCounter();
private final int pageSize;
private final int pageShifts;
private final int chunkSize;
private final Deque<ByteBuffer> cachedNioBuffers;
int freeBytes;
PoolChunkList<T> parent;
PoolChunk<T> prev;
PoolChunk<T> next;
// ...
}PoolSubpage : Handles allocation of small sub‑pages (16 B‑28 KB). Its definition includes fields for element size, bitmap, and linked‑list pointers.
final class PoolSubpage<T> implements PoolSubpageMetric {
final PoolChunk<T> chunk;
final int elemSize;
private final int pageShifts;
private final int runOffset;
private final int runSize;
private final long[] bitmap;
PoolSubpage<T> prev;
PoolSubpage<T> next;
boolean doNotDestroy;
private int maxNumElems;
private int bitmapLength;
private int nextAvail;
private int numAvail;
// ...
}The article concludes by noting that each component will be explored in detail in subsequent posts, enabling readers to fully master Netty's memory module.
Rare Earth Juejin Tech Community
Juejin, a tech community that helps developers grow.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.