背景
内存管理介绍
内存管理的目的是合理分配内存,减少内存碎片,及时回收资源,提高内存的使用资源。
可以带着以下问题进行研究:
- 内存池管理算法是如何实现高效内存分配释放,减少内存碎片?
- 高负载下内存池不断申请/释放,如何实现弹性伸缩?
- 内存池作为全局数据,在多线程环境下如何减少锁竞争?
常见的一些算法有slab,buddy,jemalloc等经典算法。
Netty中的内存管理应该是借鉴了FreeBSD内存管理的思想——jemalloc。Netty内存分配过程中总体遵循以下规则:
- 优先从缓存中分配
- 如果缓存中没有的话,从内存池看看有没有剩余可用的
- 如果已申请的没有的话,再真正申请内存
- 分段管理,每个内存大小范围使用不同的分配策略
-
内存分享
Netty根据每次请求分配内存的大小,将请求分为如下几类:
分类 | 说明 | 对应代码 | |
---|---|---|---|
tiny | 16的倍数 小于 512b | ||
small | 512b的倍数 小于等于4096b | ||
normal | 8192倍数 小于16m | ||
huge | 大于16m |
int normalizeCapacity(int reqCapacity) {
if (reqCapacity < 0) {
throw new IllegalArgumentException("capacity: " + reqCapacity + " (expected: 0+)");
}
if (reqCapacity >= chunkSize) {
return directMemoryCacheAlignment == 0 ? reqCapacity : alignCapacity(reqCapacity);
}
// 判断是否小于512b 判断算法是 (reqCapacity & 0xFFFFFE00) == 0; 0xFFFFFE00 = -512
if (!isTiny(reqCapacity)) { // >= 512
// 如果申请的内存大于512则规范化为大于reqCapacity的最近的2的指数次的值
int normalizedCapacity = reqCapacity;
normalizedCapacity --;
normalizedCapacity |= normalizedCapacity >>> 1;
normalizedCapacity |= normalizedCapacity >>> 2;
normalizedCapacity |= normalizedCapacity >>> 4;
normalizedCapacity |= normalizedCapacity >>> 8;
normalizedCapacity |= normalizedCapacity >>> 16;
normalizedCapacity ++;
if (normalizedCapacity < 0) {
normalizedCapacity >>>= 1;
}
assert directMemoryCacheAlignment == 0 || (normalizedCapacity & directMemoryCacheAlignmentMask) == 0;
return normalizedCapacity;
}
if (directMemoryCacheAlignment > 0) {
return alignCapacity(reqCapacity);
}
下面之所以是16的倍数是因为用来管理tiny内存tinySubpagePools数组的大小刚好是512>>>4,32个元素
每个元素PoolSubpage本身会构成链表,也就是说每个元素(PoolSubpage)对应的链表内每个元素的内存块大小(elemSize)是相同的,数组内每个链表的elemSize依次是:
16,32,48......480,496,512
// 刚好是16的倍数 直接返回
if ((reqCapacity & 15) == 0) {
return reqCapacity;
}
return (reqCapacity & ~15) + 16;
}
flink
org.apache.flink.runtime.io.network.netty.NettyServer
spark
org.apache.spark.network.server.TransportServer
参考: