Netty源码-PooledByteBufAllocator静态变量初始化

1 概述

众所周知,采用直接内存(off-heap)可以避免内存拷贝,因此在NIO编程中大多会采用直接内存,但是直接内存的分配和回收代价较高,Netty为了提高内存分配和回收的效率,采用了内存池机制,也即预先分配一大块内存,然后采用自己实现的内存分配算法在预先分配的内存块中进行内存空间分配和回收。当然Netty不仅对直接内存采用了内存池技术,对堆内存也使用了内存池技术。

PooledByteBufAllocator是Netty中的默认ByteBuf分配器,负责分配所需的ByteBuf,从其名字可知PooledByteBufAllocator是采用内存池进行内存分配的。但是并不是由PooledByteBufAllocator分配的ByteBuf都是采用内存池方式分配的,在PooledByteBufAllocator的静态初始化语句块会进行内存、配置等的检查,如果有不通过项或者达不到系统预设条件,则还是会跳过内存池技术进行堆或直接内存的直接分配。

2 重要静态变量

PooledByteBufAllocator中的重要静态变量如下:

//PooledByteBufAllocator
//首先要有个总体上的概念,不管是直接内存还是堆内存,
//都是由多个Page组成Chunk,每个Chunk对应一个直接内存ByteBuffer
//(注意这里是Java原生ByteBuffer)或者内存数组,多个Chunk则组成
//一个Arena,内存分配是在Arena上进行分配的,分配时会具体从Arena的
//某个Chunk中分配满足大小的页

//默认的堆内内存Arena个数
private static final int DEFAULT_NUM_HEAP_ARENA;
//默认的堆外内存(直接内存)Arena个数
private static final int DEFAULT_NUM_DIRECT_ARENA;

//默认的Page(页)字节数
private static final int DEFAULT_PAGE_SIZE;
//order决定了chunk的大小,chunk大小等于page大小<<order
// 8192 << 11 = 16 MiB per chunk
private static final int DEFAULT_MAX_ORDER; 
//下面的缓存暂时不介绍,后面介绍内存池内存分配后再介绍
private static final int DEFAULT_TINY_CACHE_SIZE;
private static final int DEFAULT_SMALL_CACHE_SIZE;
private static final int DEFAULT_NORMAL_CACHE_SIZE;
private static final int DEFAULT_MAX_CACHED_BUFFER_CAPACITY;
private static final int DEFAULT_CACHE_TRIM_INTERVAL;
private static final boolean DEFAULT_USE_CACHE_FOR_ALL_THREADS;
private static final int DEFAULT_DIRECT_MEMORY_CACHE_ALIGNMENT;
//最小的page字节数
private static final int MIN_PAGE_SIZE = 4096;
//每个chunk的最大字节数,这个最大值会约束page和order的大小
//默认等于1G
private static final int MAX_CHUNK_SIZE = (int) (((long) Integer.MAX_VALUE + 1) / 2);

3 静态初始化语句块的参数初始化

//从配置参数io.netty.allocator.pageSize获取用户
//指定的page大小,如果没有配置则为8192,即8k
int defaultPageSize = SystemPropertyUtil.getInt("io.netty.allocator.pageSize", 8192);
Throwable pageSizeFallbackCause = null;
try {
    //这里主要检查指定的page大小要是2的指数方并且大于
    //上面介绍的page大小的最小值MIN_PAGE_SIZE,即最小4k
    validateAndCalculatePageShifts(defaultPageSize);
} catch (Throwable t) {
    //如果不满足上面的验证,则page大小为默认值8k
    pageSizeFallbackCause = t;
    defaultPageSize = 8192;
}
DEFAULT_PAGE_SIZE = defaultPageSize;


private static int validateAndCalculatePageShifts(int pageSize) {
    //page大小要小于MIN_PAGE_SIZE,4k
    if (pageSize < MIN_PAGE_SIZE) {
        throw new IllegalArgumentException("pageSize: " + pageSize + " (expected: " + MIN_PAGE_SIZE + ")");
    }

    //必须为2的指数
    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);
}
//上面介绍了chunk大小等于page大小<<order
//这里则计算order大小
//首先从参数io.netty.allocator.maxOrder读取用户配置
//默认11
int defaultMaxOrder = SystemPropertyUtil.getInt("io.netty.allocator.maxOrder", 11);
Throwable maxOrderFallbackCause = null;
try {
    //这里传入上面用户指定或者默认的order,以及刚计算出的page大小
    //检验order小于14,并且page << order小于最大chunk大小
    //MAX_CHUNK_SIZE,1G
    validateAndCalculateChunkSize(DEFAULT_PAGE_SIZE, defaultMaxOrder);
} catch (Throwable t) {
    maxOrderFallbackCause = t;
    defaultMaxOrder = 11;
}
DEFAULT_MAX_ORDER = defaultMaxOrder;



private static int validateAndCalculateChunkSize(int pageSize, int maxOrder) {
    //order不能大于14
    if (maxOrder > 14) {
        throw new IllegalArgumentException("maxOrder: " + maxOrder + " (expected: 0-14)");
    }
    //保证page << order,不大于最大的chunk大小,MAX_CHUNK_SIZE
    // Ensure the resulting chunkSize does not overflow.
    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;
}

//默认最小arena数量Wie处理器个数*2
final int defaultMinNumArena = NettyRuntime.availableProcessors() * 2;
//默认的chunk大小为page << order
final int defaultChunkSize = DEFAULT_PAGE_SIZE << DEFAULT_MAX_ORDER;


//从参数io.netty.allocator.numHeapArenas读取用户指定的堆arena
//个数,如果没有指定,则取上面defaultMinNumArena和
//runtime.maxMemory() / defaultChunkSize / 2 / 3二者较小值
//这里除以2是为了保证【堆内内存池】arena大小不能超过可用【堆内存】
//的一半,除以3是为了保证每个arena中至少有三个chunk
DEFAULT_NUM_HEAP_ARENA = Math.max(0,
        SystemPropertyUtil.getInt(
                "io.netty.allocator.numHeapArenas",
                (int) Math.min(
                        defaultMinNumArena,
                        runtime.maxMemory() / defaultChunkSize / 2 / 3)));

//和上面同理
//但是这里保证的【直接内存池】arena不超过配置的【最大直接内存】的一半
DEFAULT_NUM_DIRECT_ARENA = Math.max(0,
        SystemPropertyUtil.getInt(
                "io.netty.allocator.numDirectArenas",
                (int) Math.min(
                        defaultMinNumArena,
                        PlatformDependent.maxDirectMemory() / defaultChunkSize / 2 / 3)));

//如果按照page=8k, order=11,则chunk = 8k << 11 = 16M
//那么16 * 2 * 3 = 96M,所以默认情况下最少配置96M的堆内存或者直接
//内存才会启用内存池

静态初始化语句块还有决定上面各种Cache大小的语句,本文不介绍。

//Netty启动之后,可以关注下面log输出,可以知道内存池的启用情况
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.useCacheForAllThreads: {}", DEFAULT_USE_CACHE_FOR_ALL_THREADS);
}

上面省略未介绍的DEFAULT_TINY_CACHE_SIZE等变量和PoolThreadCache有关,放在笔者文章Netty源码-PoolThreadCache中介绍。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,542评论 6 504
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,822评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,912评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,449评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,500评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,370评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,193评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,074评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,505评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,722评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,841评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,569评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,168评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,783评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,918评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,962评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,781评论 2 354

推荐阅读更多精彩内容