前言
PooledByteBufAllocator作爲池化內存分配的入口,提供了衆多的配置參數和便捷方法。這篇主要擼下他們大體都啥含義、幹啥用的。爲後面池化內存其他組件做鋪墊。
下面的成員變量基本都提供了默認值,可以通過參數去自定義,下面表格給出具體說明。
成員變量 | 說明 |
---|---|
DEFAULT_NUM_HEAP_ARENA | PoolAreana(堆內存)個數,默認爲核數的2倍,可以由參數-Dio.netty.allocator.numHeapArenas指定 |
DEFAULT_NUM_DIRECT_ARENA | PoolAreana(堆外內存)個數默認爲核數的2倍,堆外內存,可以通過-Dio.netty.allocator.numDirectArenas指定 |
DEFAULT_PAGE_SIZE | 默認pageSize=8K,可以通過-Dio.netty.allocator.pageSize,需大於4096且爲2的倍數 |
DEFAULT_MAX_ORDER | 二叉樹最高層數,取值範圍爲0~14,默認爲11,可以通過-Dio.netty.allocator.maxOrder參數指定 |
DEFAULT_TINY_CACHE_SIZE | 默認tiny類型緩存池大小512,可以通過-Dio.netty.allocator.tinyCacheSize指定 |
DEFAULT_SMALL_CACHE_SIZE | 默認small類型緩存池大小爲256,可以通過-Dio.netty.allocator.smallCacheSize指定 |
DEFAULT_NORMAL_CACHE_SIZE | 默認normal類型緩存池大小爲64,可以通過-Dio.netty.allocator.normalCacheSize指定 |
DEFAULT_MAX_CACHED_BUFFER_CAPACITY | 默認爲32KB,用於限制normal緩存數組的長度,可以通過-Dio.netty.allocator.maxCachedBufferCapacity指定 |
DEFAULT_CACHE_TRIM_INTERVAL | 默認8192,分配次數閾值,超過後釋放內存池,可以通過-Dio.netty.allocator.cacheTrimInterval指定 |
DEFAULT_CACHE_TRIM_INTERVAL_MILLIS | 默認0不開啓,定時釋放內存池,可以通過-Dio.netty.allocator.cacheTrimIntervalMillis指定 |
DEFAULT_USE_CACHE_FOR_ALL_THREADS | 默認true,使用線程緩存,可以通過-Dio.netty.allocator.useCacheForAllThread制定 |
DEFAULT_DIRECT_MEMORY_CACHE_ALIGNMENT | 直接內存的校準對齊參數,分配內存時按位與(&)校準。默認0不校準,可以通過-Dio.netty.allocator.directMemoryCacheAlignment指定 |
DEFAULT_MAX_CACHED_BYTEBUFFERS_PER_CHUNK | 默認1023,指定PoolChunk緩存ByteBuffer對象的最大數量,可以通過-Dio.netty.allocator.maxCachedByteBuffersPerChunk指定 |
MIN_PAGE_SIZE | 校驗用的,PageSize不能小於4KB |
MAX_CHUNK_SIZE | 校驗用的,Chunk的邊界值,(((long) Integer.MAX_VALUE + 1) / 2) |
heapArenas | Arena數組,元素爲HeapArena |
directArenas | Arena數組,元素爲DirectArena |
PooledByteBufAllocatorMetric metric | 暴露統計指標,例如:用了多少堆內存、用了多少堆外直接內存等 |
DEFAULT_PAGE_SIZE
下面是通過static{}靜態塊賦值PageSize,默認爲8KB,可以通過-Dio.netty.allocator.pageSize自定義。
static {
int defaultPageSize = SystemPropertyUtil.getInt("io.netty.allocator.pageSize", 8192);
Throwable pageSizeFallbackCause = null;
try {
validateAndCalculatePageShifts(defaultPageSize);
} catch (Throwable t) {
pageSizeFallbackCause = t;
defaultPageSize = 8192;
}
DEFAULT_PAGE_SIZE = defaultPageSize;
PageSize校驗過程,不能小於MIN_PAGE_SIZE(4KB),需要2的倍數。
private static int validateAndCalculatePageShifts(int pageSize) {
if (pageSize < MIN_PAGE_SIZE) {
throw new IllegalArgumentException("pageSize: " + pageSize + " (expected: " + MIN_PAGE_SIZE + ")");
}
if ((pageSize & pageSize - 1) != 0) {
throw new IllegalArgumentException("pageSize: " + pageSize + " (expected: power of 2)");
}
// Logarithm base 2. At this point we know that pageSize is a power of two.
return Integer.SIZE - 1 - Integer.numberOfLeadingZeros(pageSize);
}
DEFAULT_MAX_ORDER
maxOrder默認大小爲11,可以通過-Dio.netty.allocator.maxOrder自定義。
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;
}
DEFAULT_MAX_ORDER = defaultMaxOrder;
maxOrder校驗過程,最大值不能超過14,同時計算了ChunkSize <<=1 ,即:8192<<=1 爲 16777216(16M),也就是默認ChunkSize大小爲16M。
private static int validateAndCalculateChunkSize(int pageSize, int maxOrder) {
if (maxOrder > 14) {
throw new IllegalArgumentException("maxOrder: " + maxOrder + " (expected: 0-14)");
}
int chunkSize = pageSize;
for (int i = maxOrder; i > 0; i --) {
if (chunkSize > MAX_CHUNK_SIZE / 2) {
throw new IllegalArgumentException(String.format(
"pageSize (%d) << maxOrder (%d) must not exceed %d", pageSize, maxOrder, MAX_CHUNK_SIZE));
}
chunkSize <<= 1;
}
return chunkSize;
}
DEFAULT_NUM_HEAP_ARENA和DEFAULT_NUM_DIRECT_ARENA
final Runtime runtime = Runtime.getRuntime();
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)));
解讀: DEFAULT_NUM_HEAP_ARENA與DEFAULT_NUM_DIRECT_ARENA賦值結構相同,默認值也相同。DEFAULT_NUM_HEAP_ARENA通過-Dio.netty.allocator.numHeapArenas自定義;DEFAULT_NUM_DIRECT_ARENA通過-Dio.netty.allocator.numDirectArenas自定義。
參數 | 含義 |
---|---|
defaultMinNumArena | 默認爲CPU核數的2倍 |
defaultChunkSize | DEFAULT_PAGE_SIZE << DEFAULT_MAX_ORDER 也就是 8192 << 11 = 16777216(16M) |
runtime.maxMemory() | Jvm從操作系統獲取的最大內存由參數-Xmx指定 |
runtime.maxMemory() / defaultChunkSize / 2 / 3 | defaultChunkSize=16M,可以簡化爲 runtime.maxMemory()/96M。也就是Jvm最大內存/96M |
所以默認DEFAULT_NUM_HEAP_ARENA=DEFAULT_NUM_DIRECT_ARENA,CPU核數2倍與runtime.maxMemory()/96M取最小值,在高配的環境下,通常爲核數的兩倍。
其他
下面這幾個,都是可以通過參數指定,沒啥特別邏輯,具體含義見上面成員變量。
DEFAULT_TINY_CACHE_SIZE = SystemPropertyUtil.getInt("io.netty.allocator.tinyCacheSize", 512);
DEFAULT_SMALL_CACHE_SIZE = SystemPropertyUtil.getInt("io.netty.allocator.smallCacheSize", 256);
DEFAULT_NORMAL_CACHE_SIZE = SystemPropertyUtil.getInt("io.netty.allocator.normalCacheSize", 64);
DEFAULT_MAX_CACHED_BUFFER_CAPACITY = SystemPropertyUtil.getInt(
"io.netty.allocator.maxCachedBufferCapacity", 32 * 1024);
DEFAULT_CACHE_TRIM_INTERVAL = SystemPropertyUtil.getInt(
"io.netty.allocator.cacheTrimInterval", 8192);
DEFAULT_CACHE_TRIM_INTERVAL_MILLIS = SystemPropertyUtil.getLong(
"io.netty.allocator.cacheTrimIntervalMillis", 0);
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);
DEFAULT_MAX_CACHED_BYTEBUFFERS_PER_CHUNK = SystemPropertyUtil.getInt(
"io.netty.allocator.maxCachedByteBuffersPerChunk", 1023);
下面對構造函數的賦值和校驗進行走查。
public PooledByteBufAllocator(boolean preferDirect, int nHeapArena, int nDirectArena, int pageSize, int maxOrder,
int tinyCacheSize, int smallCacheSize, int normalCacheSize,
boolean useCacheForAllThreads, int directMemoryCacheAlignment) {
// 指定是否使用直接內存
super(preferDirect);
// 創建PoolThreadLocalCache,useCacheForAllThreads是否允許使用對象池
threadCache = new PoolThreadLocalCache(useCacheForAllThreads);
// tiny類型緩存大小
this.tinyCacheSize = tinyCacheSize;
// small類型緩存大小
this.smallCacheSize = smallCacheSize;
// normal類型緩存大小
this.normalCacheSize = normalCacheSize;
// chunk大小默認16M
chunkSize = validateAndCalculateChunkSize(pageSize, maxOrder);
// 需正數
checkPositiveOrZero(nHeapArena, "nHeapArena");
checkPositiveOrZero(nDirectArena, "nDirectArena");
checkPositiveOrZero(directMemoryCacheAlignment, "directMemoryCacheAlignment");
if (directMemoryCacheAlignment > 0 && !isDirectMemoryCacheAlignmentSupported()) {
throw new IllegalArgumentException("directMemoryCacheAlignment is not supported");
}
if ((directMemoryCacheAlignment & -directMemoryCacheAlignment) != directMemoryCacheAlignment) {
throw new IllegalArgumentException("directMemoryCacheAlignment: "
+ directMemoryCacheAlignment + " (expected: power of two)");
}
// 頁偏移默認爲13,pageShift = Integer.SIZE - 1 - Integer.numberOfLeadingZeros(8192) = 32 - 1 - 18 = 13
int pageShifts = validateAndCalculatePageShifts(pageSize);
if (nHeapArena > 0) {
// 堆內存,創建PoolArena[]數組
heapArenas = newArenaArray(nHeapArena);
List<PoolArenaMetric> metrics = new ArrayList<PoolArenaMetric>(heapArenas.length);
// 給數組元素賦值HeapArena
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();
}
if (nDirectArena > 0) {
// 堆外直接內存,創建PoolArena[]數組
directArenas = newArenaArray(nDirectArena);
List<PoolArenaMetric> metrics = new ArrayList<PoolArenaMetric>(directArenas.length);
// 直接內存數組賦值DirectArena
for (int i = 0; i < directArenas.length; i ++) {
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);
}
比較重要的方法主要是newHeapBuffer&newDirectBuffer,這兩個的流程和代碼結構一致,大體流程見上一篇文末有梳理。
另外,metric這個方法可以觀察到內存相關情況。
public PooledByteBufAllocatorMetric metric() {
return metric;
}
usedHeapMemory ,查看Netty內存池分配堆空間大小。
final long usedHeapMemory() {
return usedMemory(heapArenas);
}
usedDirectMemory, 查看Netty內存池分配堆外直接空間大小。
final long usedDirectMemory() {
return usedMemory(directArenas);
}
通過usedMemory累加PoolArena的內存分配。
private static long usedMemory(PoolArena<?>[] arenas) {
if (arenas == null) {
return -1;
}
long used = 0;
for (PoolArena<?> arena : arenas) {
used += arena.numActiveBytes();
if (used < 0) {
return Long.MAX_VALUE;
}
}
return used;
}
關注公衆號,回覆「老梁」獲取所有文章
本文分享自微信公衆號 - 瓜農老梁(gh_01130ae30a83)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。