随笔分类
解读内存池
解读 PooledByteBufAllocator
对于池化内存管理器 (PooledByteBufAllocator)而言,其内部存在两种类型的 Arena,一种是 Heap类型的 Arena,另一种则是 Direct类型的 Arena,Arena的引入其实为了解决 cpu缓存行冲突导致的数据不一致需要进行同步带来的开销问题而引入的,对于 heapArena、directArena的数目其实适合 cpu核心数有关的,通常情况下是 cpu核心数 * 2
这里还涉及到了 Netty中规定的一些规格,如 Netty中默认 pageSize是 8kb
Arena内部其实管理着多个不同蕴意的 PoolChunkList,PoolChunkList中存储着便是 PoolChunk;Netty默认的 Chunk大小是 16mb (Netty对其最大占用内存进行了限制,最大为 1GB),Chunk内部会去使用一颗满二叉树去表示其内存的占用情况,而这颗满二叉树的默认最大深度便是 11;PoolChunk其实是内存池真正持有底层内存的对象
Arena中的 PoolChunkList会根据 Chunk使用率的不同串成一个双向链表,也就是 Chunk的存放位置不是固定的,会根据其使用率的情况存放到具体的 PoolChunkList中去,对应的有 qinit,q000,q025,q050,q075,q100,Chunk使用率从低到高
而通常情况下,Netty更加偏向于去分配 direct内存,因此我们更加关注的也是对 direct类型 Arena的分析!
而且 Arena中存在两种类型的 SubpagePools,即 TinySubpagePools以及 SmallSubpagePools,前者对应着 32中规格,后者对应着 4种规格
字段
// 池化内存缓冲区分配器
public class PooledByteBufAllocator extends AbstractByteBufAllocator implements ByteBufAllocatorMetricProvider {
private static final InternalLogger logger = InternalLoggerFactory.getInstance(PooledByteBufAllocator.class);
// 默认的 HeapArena数目:cpu核心数 * 2
private static final int DEFAULT_NUM_HEAP_ARENA;
// 默认的 DirectArena数目:cpu核心数 * 2
private static final int DEFAULT_NUM_DIRECT_ARENA;
// 表示 Netty中默认的页大小 - 8kb
private static final int DEFAULT_PAGE_SIZE;
// 表示 chunk中满二叉树的最大深度 - 11
// Chunk内部使用满二叉树来去表示其管理的内存的占用情况
private static final int DEFAULT_MAX_ORDER; // 8192 << 11 = 16 MiB per chunk
// 表示 TinyMemoryRegionCache内部可以缓存 512个内存位置信息
private static final int DEFAULT_TINY_CACHE_SIZE;
// 表示 SmallMemoryRegionCache内部可以缓存 256个内存位置信息
private static final int DEFAULT_SMALL_CACHE_SIZE;
// 表示 NormalMemoryRegionCacheSize内部可以缓存 64个内存位置信息
private static final int DEFAULT_NORMAL_CACHE_SIZE;
// 32k, 表示 MemoryRegionCache允许缓存的最大内存规格:32k - 如, NormalMemoryCacheSize
static final int DEFAULT_MAX_CACHED_BUFFER_CAPACITY;
// 8192
private static final int DEFAULT_CACHE_TRIM_INTERVAL;
private static final long DEFAULT_CACHE_TRIM_INTERVAL_MILLIS;
// 是否全部线程都可以使用 PoolThreadCache技术 - 默认 true
private static final boolean DEFAULT_USE_CACHE_FOR_ALL_THREADS;
// 表示默认的 Direct内存对齐基准
private static final int DEFAULT_DIRECT_MEMORY_CACHE_ALIGNMENT;
static final int DEFAULT_MAX_CACHED_BYTEBUFFERS_PER_CHUNK;
// 规定的 page最小值 - 2kb
private static final int MIN_PAGE_SIZE = 4096;
// 规定的 chunk内存最大值: 1GB
private static final int MAX_CHUNK_SIZE = (int) (((long) Integer.MAX_VALUE + 1) / 2);
// 参数:是否偏向去使用堆外内存
// 一般而言, 只要平台能够去使用堆外内存, 都会是偏向去使用堆外内存 - true
public static final PooledByteBufAllocator DEFAULT =
new PooledByteBufAllocator(PlatformDependent.directBufferPreferred());
// 表示 Heap类型的 Arena
private final PoolArena<byte[]>[] heapArenas;
// 表示 Direct类型的 Arena
private final PoolArena<ByteBuffer>[] directArenas;
private final int tinyCacheSize;
// 表示 SmallMemoryRegionCache可以去存放的内存位置信息 - 256
private final int smallCacheSize;
// 表示 NormalMemoryRegionCache可以去存放的内存位置信息 - 64
private final int normalCacheSize;
private final List<PoolArenaMetric> heapArenaMetrics;
private final List<PoolArenaMetric> directArenaMetrics;
// threadCache非常类似于 ThreadLocal, 每个线程都可以到 threadCache中去获取与当前线程相关的 PoolThreadCache
private final PoolThreadLocalCache threadCache;
// 表示计算出来的 chunk大小
private final int chunkSize;
private final PooledByteBufAllocatorMetric metric;
静态代码块
static {
// 获取默认页大小:8kb
int defaultPageSize = SystemPropertyUtil.getInt("io.netty.allocator.pageSize", 8192);
Throwable pageSizeFallbackCause = null;
try {
validateAndCalculatePageShifts(defaultPageSize);
} catch (Throwable t) {
pageSizeFallbackCause = t;
defaultPageSize = 8192;
}
// 设置 Netty默认的页大小:8kb
DEFAULT_PAGE_SIZE = defaultPageSize;
// PoolChunk内部使用一颗满二叉树表示内存占用情况, 这棵树的最大深度便是 11
int defaultMaxOrder = SystemPropertyUtil.getInt("io.netty.allocator.maxOrder", 11);
Throwable maxOrderFallbackCause = null;
try {
validateAndCalculateChunkSize(DEFAULT_PAGE_SIZE, defaultMaxOrder);
} catch (Throwable t) {
maxOrderFallbackCause = t;
defaultMaxOrder = 11;
}
// 设置 Chunk内部满二叉树的最大深度 - 11
DEFAULT_MAX_ORDER = defaultMaxOrder;
// Determine reasonable default for nHeapArena and nDirectArena.
// Assuming each arena has 3 chunks, the pool should not consume more than 50% of max memory.
final Runtime runtime = Runtime.getRuntime();
/*
* We use 2 * available processors by default to reduce contention as we use 2 * available processors for the
* number of EventLoops in NIO and EPOLL as well. If we choose a smaller number we will run into hot spots as
* allocation and de-allocation needs to be synchronized on the PoolArena.
*
* See https://github.com/netty/netty/issues/3888.
*/
// 计算出默认最少的 Arena数目:cpu核心数 * 2
final int defaultMinNumArena = NettyRuntime.availableProcessors() * 2;
// 计算出默认 Chunk大小:16mb - 8kb << 11
// 默认情况下, chunk管理着 16mb内存
final int defaultChunkSize = DEFAULT_PAGE_SIZE << DEFAULT_MAX_ORDER;
// 这里会来计算出 Arena数目, 我们认为是 cpu核心数 * 2
// 默认的 HeapArena数目:cpu核心数 * 2
DEFAULT_NUM_HEAP_ARENA = Math.max(0,
SystemPropertyUtil.getInt(
"io.netty.allocator.numHeapArenas",
(int) Math.min(
defaultMinNumArena,
runtime.maxMemory() / defaultChunkSize / 2 / 3)));
// 默认的 DirectArena数目:cpu核心数 * 2
DEFAULT_NUM_DIRECT_ARENA = Math.max(0,
SystemPropertyUtil.getInt(
"io.netty.allocator.numDirectArenas",
(int) Math.min(
defaultMinNumArena,
PlatformDependent.maxDirectMemory() / defaultChunkSize / 2 / 3)));
// cache sizes
// 表示 TinyMemoryRegionCache内部可以缓存 512个内存位置信息
DEFAULT_TINY_CACHE_SIZE = SystemPropertyUtil.getInt("io.netty.allocator.tinyCacheSize", 512);
// cache sizes
// 表示 SmallMemoryRegionCache内部可以缓存 256个内存位置信息
DEFAULT_SMALL_CACHE_SIZE = SystemPropertyUtil.getInt("io.netty.allocator.smallCacheSize", 256);
// 表示 NormalMemoryRegionCacheSize内部可以缓存 64个内存位置信息
DEFAULT_NORMAL_CACHE_SIZE = SystemPropertyUtil.getInt("io.netty.allocator.normalCacheSize", 64);
// 32 kb is the default maximum capacity of the cached buffer. Similar to what is explained in
// 'Scalable memory allocation using jemalloc'
// 32k, 表示 MemoryRegionCache允许缓存的最大内存规格:32k - 如, NormalMemoryCacheSize
DEFAULT_MAX_CACHED_BUFFER_CAPACITY = SystemPropertyUtil.getInt(
"io.netty.allocator.maxCachedBufferCapacity", 32 * 1024);
// the number of threshold of allocations when cached entries will be freed up if not frequently used
DEFAULT_CACHE_TRIM_INTERVAL = SystemPropertyUtil.getInt(
"io.netty.allocator.cacheTrimInterval", 8192);
DEFAULT_CACHE_TRIM_INTERVAL_MILLIS = SystemPropertyUtil.getLong(
"io.netty.allocation.cacheTrimIntervalMillis", 0);
// 是否全部线程都可以使用 PoolThreadCache技术 - 默认 true
DEFAULT_USE_CACHE_FOR_ALL_THREADS = SystemPropertyUtil.getBoolean(
"io.netty.allocator.useCacheForAllThreads", true);
DEFAULT_DIRECT_MEMORY_CACHE_ALIGNMENT = SystemPropertyUtil.getInt(
"io.netty.allocator.directMemoryCacheAlignment", 0);
// Use 1023 by default as we use an ArrayDeque as backing storage which will then allocate an internal array
// of 1024 elements. Otherwise we would allocate 2048 and only use 1024 which is wasteful.
DEFAULT_MAX_CACHED_BYTEBUFFERS_PER_CHUNK = SystemPropertyUtil.getInt(
"io.netty.allocator.maxCachedByteBuffersPerChunk", 1023);
if (logger.isDebugEnabled()) {
logger.debug("-Dio.netty.allocator.numHeapArenas: {}", DEFAULT_NUM_HEAP_ARENA);
logger.debug("-Dio.netty.allocator.numDirectArenas: {}", DEFAULT_NUM_DIRECT_ARENA);
if (pageSizeFallbackCause == null) {
logger.debug("-Dio.netty.allocator.pageSize: {}", DEFAULT_PAGE_SIZE);
} else {
logger.debug("-Dio.netty.allocator.pageSize: {}", DEFAULT_PAGE_SIZE, pageSizeFallbackCause);
}
if (maxOrderFallbackCause == null) {
logger.debug("-Dio.netty.allocator.maxOrder: {}", DEFAULT_MAX_ORDER);
} else {
logger.debug("-Dio.netty.allocator.maxOrder: {}", DEFAULT_MAX_ORDER, maxOrderFallbackCause);
}
logger.debug("-Dio.netty.allocator.chunkSize: {}", DEFAULT_PAGE_SIZE << DEFAULT_MAX_ORDER);
logger.debug("-Dio.netty.allocator.tinyCacheSize: {}", DEFAULT_TINY_CACHE_SIZE);
logger.debug("-Dio.netty.allocator.smallCacheSize: {}", DEFAULT_SMALL_CACHE_SIZE);
logger.debug("-Dio.netty.allocator.normalCacheSize: {}", DEFAULT_NORMAL_CACHE_SIZE);
logger.debug("-Dio.netty.allocator.maxCachedBufferCapacity: {}", DEFAULT_MAX_CACHED_BUFFER_CAPACITY);
logger.debug("-Dio.netty.allocator.cacheTrimInterval: {}", DEFAULT_CACHE_TRIM_INTERVAL);
logger.debug("-Dio.netty.allocator.cacheTrimIntervalMillis: {}", DEFAULT_CACHE_TRIM_INTERVAL_MILLIS);
logger.debug("-Dio.netty.allocator.useCacheForAllThreads: {}", DEFAULT_USE_CACHE_FOR_ALL_THREADS);
logger.debug("-Dio.netty.allocator.maxCachedByteBuffersPerChunk: {}",
DEFAULT_MAX_CACHED_BYTEBUFFERS_PER_CHUNK);
}
}
构造
// 参数:是否偏向去使用堆外内存
// 一般而言, 只要平台能够去使用堆外内存, 都会是偏向去使用堆外内存 - true
public static final PooledByteBufAllocator DEFAULT =
new PooledByteBufAllocator(PlatformDependent.directBufferPreferred());
由这,延伸出:
// 参数:是否偏向去使用堆外内存
// 一般而言, 只要平台能够去使用堆外内存, 都会是偏向去使用堆外内存 - true
@SuppressWarnings("deprecation")
public PooledByteBufAllocator(boolean preferDirect) {
// 参数一:true - 偏向去使用堆外内存
// 参数二:默认的 HeapArena数目:cpu核心数 * 2
// 参数三:默认的 DirectArena数目:cpu核心数 * 2
// 参数四:默认页大小:8kb
// 参数五:chunk中满二叉树的最大深度:11
this(preferDirect, DEFAULT_NUM_HEAP_ARENA, DEFAULT_NUM_DIRECT_ARENA, DEFAULT_PAGE_SIZE, DEFAULT_MAX_ORDER);
}
/**
* @deprecated use
* {@link PooledByteBufAllocator#PooledByteBufAllocator(boolean, int, int, int, int, int, int, int, boolean)}
*/
// 参数一:true - 偏向去使用堆外内存
// 参数二:默认的 HeapArena数目:cpu核心数 * 2
// 参数三:默认的 DirectArena数目:cpu核心数 * 2
// 参数四:默认页大小:8kb
// 参数五:chunk中满二叉树的最大深度:11
@Deprecated
public PooledByteBufAllocator(boolean preferDirect, int nHeapArena, int nDirectArena, int pageSize, int maxOrder) {
// 参数一:true - 偏向去使用堆外内存
// 参数二:默认的 HeapArena数目:cpu核心数 * 2
// 参数三:默认的 DirectArena数目:cpu核心数 * 2
// 参数四:默认页大小:8kb
// 参数五:chunk中满二叉树的最大深度:11
// 参数六:512, 表示 TinyMemoryRegionSize里可以去缓存 512个内存位置信息
// 参数七:表示 SmallMemoryRegionCache内部可以缓存 256个内存位置信息
// 参数八:表示 NormalMemoryRegionCacheSize内部可以缓存 64个内存位置信息
this(preferDirect, nHeapArena, nDirectArena, pageSize, maxOrder,
DEFAULT_TINY_CACHE_SIZE, DEFAULT_SMALL_CACHE_SIZE, DEFAULT_NORMAL_CACHE_SIZE);
}
/**
* @deprecated use
* {@link PooledByteBufAllocator#PooledByteBufAllocator(boolean, int, int, int, int, int, int, int, boolean)}
*/
// 参数一:true - 偏向去使用堆外内存
// 参数二:默认的 HeapArena数目:cpu核心数 * 2
// 参数三:默认的 DirectArena数目:cpu核心数 * 2
// 参数四:默认页大小:8kb
// 参数五:chunk中满二叉树的最大深度:11
// 参数六:0, 表示 TinyMemoryRegionSize里可以去缓存 0个内存位置信息
// 参数七:表示 SmallMemoryRegionCache内部可以缓存 256个内存位置信息
// 参数八:表示 NormalMemoryRegionCacheSize内部可以缓存 64个内存位置信息
@Deprecated
public PooledByteBufAllocator(boolean preferDirect, int nHeapArena, int nDirectArena, int pageSize, int maxOrder,
int tinyCacheSize, int smallCacheSize, int normalCacheSize) {
// 参数一:true - 偏向去使用堆外内存
// 参数二:默认的 HeapArena数目:cpu核心数 * 2
// 参数三:默认的 DirectArena数目:cpu核心数 * 2
// 参数四:默认页大小:8kb
// 参数五:chunk中满二叉树的最大深度:11
// 参数六:表示 TinyMemoryRegionCache内部可以缓存 512个内存位置信息
// 参数七:表示 SmallMemoryRegionCache内部可以缓存 256个内存位置信息
// 参数八:表示 NormalMemoryRegionCacheSize内部可以缓存 64个内存位置信息
// 参数九:是否所有线程都可以去使用 PoolThreadCache技术 - 默认是 true
// 参数十:表示默认的 Direct内存对齐基准 - 0
this(preferDirect, nHeapArena, nDirectArena, pageSize, maxOrder, tinyCacheSize, smallCacheSize,
normalCacheSize, DEFAULT_USE_CACHE_FOR_ALL_THREADS, DEFAULT_DIRECT_MEMORY_CACHE_ALIGNMENT);
}
// 参数一:true - 偏向去使用堆外内存
// 参数二:默认的 HeapArena数目:cpu核心数 * 2
// 参数三:默认的 DirectArena数目:cpu核心数 * 2
// 参数四:默认页大小:8kb
// 参数五:chunk中满二叉树的最大深度:11
// 参数六:表示 TinyMemoryRegionCache内部可以缓存 512 个内存位置信息
// 参数七:表示 SmallMemoryRegionCache内部可以缓存 256个内存位置信息
// 参数八:表示 NormalMemoryRegionCacheSize内部可以缓存 64个内存位置信息
// 参数九:是否所有线程都可以去使用 PoolThreadCache技术 - 默认是 true
// 参数十:表示默认的 Direct内存对齐基准 - 0
public PooledByteBufAllocator(boolean preferDirect, int nHeapArena, int nDirectArena, int pageSize, int maxOrder,
int tinyCacheSize, int smallCacheSize, int normalCacheSize,
boolean useCacheForAllThreads, int directMemoryCacheAlignment) {
// 参数:true
// 将申请偏向堆 或 非堆的信息交给父类, 由父类根据信息进行路由的切换
super(preferDirect);
// threadCache非常类似于 ThreadLocal, 每个线程都可以到 threadCache中去获取与当前线程相关的 PoolThreadCache
threadCache = new PoolThreadLocalCache(useCacheForAllThreads);
// 设置 SmallMemoryRegionCache可以去存放的内存位置信息 -
this.tinyCacheSize = tinyCacheSize;
// 设置 SmallMemoryRegionCache可以去存放的内存位置信息 - 256
this.smallCacheSize = smallCacheSize;
// 设置 NormalMemoryRegionCache可以去存放的内存位置信息 - 64
this.normalCacheSize = normalCacheSize;
// 设置 chunk大小 - 16mb
// 根据 pageSize和 满二叉树深度值来计算出
chunkSize = validateAndCalculateChunkSize(pageSize, maxOrder);
checkPositiveOrZero(nHeapArena, "nHeapArena");
checkPositiveOrZero(nDirectArena, "nDirectArena");
checkPositiveOrZero(directMemoryCacheAlignment, "directMemoryCacheAlignment");
if (directMemoryCacheAlignment > 0 && !isDirectMemoryCacheAlignmentSupported()) {
throw new IllegalArgumentException("directMemoryCacheAlignment is not supported");
}
// 直接内存对齐基准必须是 2的次方倍 - Java中负数以补码形式进行表示
// 16 & -16 -> 10000 & 10000 -> 16
if ((directMemoryCacheAlignment & -directMemoryCacheAlignment) != directMemoryCacheAlignment) {
throw new IllegalArgumentException("directMemoryCacheAlignment: "
+ directMemoryCacheAlignment + " (expected: power of two)");
}
// 13, 表示 1向左移 13位后便能得到 pageSize的值 -> 8192 -> 8kb
int pageShifts = validateAndCalculatePageShifts(pageSize);
// true
if (nHeapArena > 0) {
// 创建出 Heap类型的 Arena
// 参数:cpu核心数 * 2
heapArenas = newArenaArray(nHeapArena);
// 监控报表相关的
List<PoolArenaMetric> metrics = new ArrayList<PoolArenaMetric>(heapArenas.length);
// for循环最终创建了 cpu * 2核心数个 heap类型的 arena, 然后存放到数组 heapArenas中去
for (int i = 0; i < heapArenas.length; i ++) {
PoolArena.HeapArena arena = new PoolArena.HeapArena(this,
pageSize, maxOrder, pageShifts, chunkSize,
directMemoryCacheAlignment);
heapArenas[i] = arena;
metrics.add(arena);
}
heapArenaMetrics = Collections.unmodifiableList(metrics);
} else {
heapArenas = null;
heapArenaMetrics = Collections.emptyList();
}
// true - 这里是我们主要关心的
if (nDirectArena > 0) {
// 创建出 Direct类型的 Arena
// 参数:cpu核心数 * 2
directArenas = newArenaArray(nDirectArena);
// 监控报表相关的
List<PoolArenaMetric> metrics = new ArrayList<PoolArenaMetric>(directArenas.length);
// for循环最终创建了 cpu * 2核心数个 direct类型的 arena, 然后存放到数组 directArenas中去
for (int i = 0; i < directArenas.length; i ++) {
// DirectArena也是我们主要去分析的路线
// 参数一:当前的池化内存分配器
// 参数二:pageSize, 8kb
// 参数三:chunk中满二叉树的最大深度, 11
// 参数四:13, 1 << 13 -> 8kb
// 参数五:chunk大小, 16mb
// 参数六:直接内存对齐基准:0
PoolArena.DirectArena arena = new PoolArena.DirectArena(
this, pageSize, maxOrder, pageShifts, chunkSize, directMemoryCacheAlignment);
directArenas[i] = arena;
metrics.add(arena);
}
directArenaMetrics = Collections.unmodifiableList(metrics);
} else {
directArenas = null;
directArenaMetrics = Collections.emptyList();
}
metric = new PooledByteBufAllocatorMetric(this);
}
AbstractByteBufAllocator
/**
* Create new instance
*
* @param preferDirect {@code true} if {@link #buffer(int)} should try to allocate a direct buffer rather than
* a heap buffer
*/
protected AbstractByteBufAllocator(boolean preferDirect) {
// 这里来设置了内存分配的倾向, 因此, 后续再去进行内存分配时根据此值来进行
directByDefault = preferDirect && PlatformDependent.hasUnsafe();
emptyBuf = new EmptyByteBuf(this);
}
可以看到,构造中主要做的事情:创建出两种类型的 Arena,即 HeapArena、DirectArena,其默认数目与 cpu核心数有关,这里我们来考虑是 cpu核心数 * 2;
接着,我们来对 DirectArena进行解读
解读 DirectArena
构造
static final class DirectArena extends PoolArena<ByteBuffer> {
// DirectArena也是我们主要去分析的路线
// 参数一:当前的池化内存分配器
// 参数二:pageSize, 8kb
// 参数三:chunk中满二叉树的最大深度, 11
// 参数四:13, 1 << 13 -> 8kb
// 参数五:chunk大小, 16mb
// 参数六:直接内存对齐基准:0
DirectArena(PooledByteBufAllocator parent, int pageSize, int maxOrder,
int pageShifts, int chunkSize, int directMemoryCacheAlignment) {
super(parent, pageSize, maxOrder, pageShifts, chunkSize,
directMemoryCacheAlignment);
}
// DirectArena也是我们主要去分析的路线
// 参数一:当前的池化内存分配器
// 参数二:pageSize, 8kb
// 参数三:chunk中满二叉树的最大深度, 11
// 参数四:13, 1 << 13 -> 8kb
// 参数五:chunk大小, 16mb
// 参数六:直接内存对齐基准:0
protected PoolArena(PooledByteBufAllocator parent, int pageSize,
int maxOrder, int pageShifts, int chunkSize, int cacheAlignment) {
// 记录当前 Arena所归属的爸爸是谁, 即池化内存管理器
this.parent = parent;
// 8kb
this.pageSize = pageSize;
// 11
this.maxOrder = maxOrder;
// 13
this.pageShifts = pageShifts;
// 16mb
this.chunkSize = chunkSize;
// 0
directMemoryCacheAlignment = cacheAlignment;
directMemoryCacheAlignmentMask = cacheAlignment - 1;
// 8k - 1 -> 0b 01 1111 1111 1111
// ~(0b 01 1111 1111 1111) -> 0b 1111 1111 1111 1111 1110 0000 0000 0000
// 作用:外部申请内存时, 如果大于 1 page, 会去和 subpageOverflowMask进行位与运算, 会得到一个 != 0的值
subpageOverflowMask = ~(pageSize - 1);
// tiny有着 32中规格, 16b, 32b, ..., 496b, 因此, 这里会来创建一个长度为 32的 PoolSubpage数组类型的池子,
// 去引用 tiny类型的 Subpage, 供 arena区域共享
tinySubpagePools = newSubpagePoolArray(numTinySubpagePools);
// 对每一个桶位进行初始化
// 每个桶内的元素是 PoolSubpage对象, 该对象为 head, head在初始时 prev、next都指向自身
for (int i = 0; i < tinySubpagePools.length; i ++) {
tinySubpagePools[i] = newSubpagePoolHead(pageSize);
}
// 4
numSmallSubpagePools = pageShifts - 9;
// small有四种规格, 512b, 1024b, 2048b, 4096b
// 这里来创建出长度为 4的 Subpage类型的数组
smallSubpagePools = newSubpagePoolArray(numSmallSubpagePools);
// 对每一个桶位进行初始化
// 每个桶内的元素是 PoolSubpage对象, 该对象为 head, head在初始时 prev、next都指向自身
for (int i = 0; i < smallSubpagePools.length; i ++) {
smallSubpagePools[i] = newSubpagePoolHead(pageSize);
}
// 接下来便是创建出 PoolChunkList的列表, 根据使用率的不同将这些 list串成一个链表
// 并且后续会根据 Chunk的使用率不同存放到不同的 PoolChunkList中去
// q100对应的 PoolChunkList使用率最高
q100 = new PoolChunkList<T>(this, null, 100, Integer.MAX_VALUE, chunkSize);
q075 = new PoolChunkList<T>(this, q100, 75, 100, chunkSize);
q050 = new PoolChunkList<T>(this, q075, 50, 100, chunkSize);
q025 = new PoolChunkList<T>(this, q050, 25, 75, chunkSize);
q000 = new PoolChunkList<T>(this, q025, 1, 50, chunkSize);
// qInit对应的 PoolChunkList使用率最低
qInit = new PoolChunkList<T>(this, q000, Integer.MIN_VALUE, 25, chunkSize);
q100.prevList(q075);
q075.prevList(q050);
q050.prevList(q025);
q025.prevList(q000);
q000.prevList(null);
qInit.prevList(qInit);
List<PoolChunkListMetric> metrics = new ArrayList<PoolChunkListMetric>(6);
metrics.add(qInit);
metrics.add(q000);
metrics.add(q025);
metrics.add(q050);
metrics.add(q075);
metrics.add(q100);
chunkListMetrics = Collections.unmodifiableList(metrics);
}
可以看到,Arena存储着一个链表,对应的便是不同使用率的 PoolChunkList串起来的,其里面存储着的便是不同使用率的 chunk
并且,Arena中存储着两种规格的内存页数组,即 tinySubpagePools、smallSubpagePools,前者对应着 32种规格,后者对应着 4种规格
并且对这两种规格的内存页数组中的每一个桶位进行了初始化,每个桶内的元素是 PoolSubpage对象, 该对象为 head, head在初始时 prev、next都指向自身
对应着:
// 对每一个桶位进行初始化
// 每个桶内的元素是 PoolSubpage对象, 该对象为 head, head在初始时 prev、next都指向自身
private PoolSubpage<T> newSubpagePoolHead(int pageSize) {
PoolSubpage<T> head = new PoolSubpage<T>(pageSize);
head.prev = head;
head.next = head;
return head;
}