PoolChunkList

概要

在Netty中, PoolArena将内存分为很多PoolChunk进行管理. 通过多个PoolChunkList根据PoolChunk的使用率进行分组保存,并将动态的移动至对应的PoolChunkList中

在PoolArena中根据使用率分如下几组PoolChunkList

private final PoolChunkList<T> q050; //存储内存利用率50-100%的PoolChunk
private final PoolChunkList<T> q025; //存储内存利用率25-75%的PoolChunk
private final PoolChunkList<T> q000; //存储内存利用率1-50%的PoolChunk
private final PoolChunkList<T> qInit; //存储内存利用率0-25%的PoolChunk
private final PoolChunkList<T> q075; //存储内存利用率75-100%的PoolChunk
private final PoolChunkList<T> q100; //存储内存利用率100%的PoolChunk

q100 = new PoolChunkList<T>(null, 100, Integer.MAX_VALUE, chunkSize);
q075 = new PoolChunkList<T>(q100, 75, 100, chunkSize);
q050 = new PoolChunkList<T>(q075, 50, 100, chunkSize);
q025 = new PoolChunkList<T>(q050, 25, 75, chunkSize);
q000 = new PoolChunkList<T>(q025, 1, 50, chunkSize);
qInit = new PoolChunkList<T>(q000, Integer.MIN_VALUE, 25, chunkSize);

q100.prevList(q075);
q075.prevList(q050);
q050.prevList(q025);
q025.prevList(q000);
q000.prevList(null);
qInit.prevList(qInit);
img

PoolChunkList类主要属性如下所示:

// 下一个ChunkList
private final PoolChunkList<T> nextList;
// 最小内存使用率
private final int minUsage;
// 最大内存使用率
private final int maxUsage;
private final int maxCapacity;
// 头节点
private PoolChunk<T> head;
// 上一个ChunkList
private PoolChunkList<T> prevList;
img

构造方法

PoolChunkList(PoolChunkList<T> nextList, int minUsage, int maxUsage, int chunkSize) {
    assert minUsage <= maxUsage;
    this.nextList = nextList;
    this.minUsage = minUsage;
    this.maxUsage = maxUsage;
    // 该list所能分配的最大容量
    maxCapacity = calculateMaxCapacity(minUsage, chunkSize);
}

private static int calculateMaxCapacity(int minUsage, int chunkSize) {
    minUsage = minUsage0(minUsage);

    if (minUsage == 100) {
        return 0;
    }
    // 计算一个chunk在除去最小的使用率的情况下所能分配的最大空间
    // Q25的maxCapacity=chunkSize*75%
    return  (int) (chunkSize * (100L - minUsage) / 100L);
}

添加

void add(PoolChunk<T> chunk) {
    // 看当前chunk的使用率是否超过了最大值, 加到下一个list
    if (chunk.usage() >= maxUsage) {
        nextList.add(chunk);
        return;
    }
    add0(chunk);
}

void add0(PoolChunk<T> chunk) {
    // 在chunk中保存所属的chunlist
    chunk.parent = this;
    // 如果还没有head, chunk来
    if (head == null) {
        head = chunk;
        chunk.prev = null;
        chunk.next = null;
    } else {
        // 将这个chunk加到list里面作为新的head
        chunk.prev = null;
        chunk.next = head;
        head.prev = chunk;
        head = chunk;
    }
}

删除

private void remove(PoolChunk<T> cur) {
    if (cur == head) {
        // 让下一个接替head
        head = cur.next;
        if (head != null) {
            head.prev = null;
        }
    } else {
        // 将cur前后解绑
        PoolChunk<T> next = cur.next;
        cur.prev.next = next;
        if (next != null) {
            next.prev = cur.prev;
        }
    }
}

分配

boolean allocate(PooledByteBuf<T> buf, int reqCapacity, int normCapacity) {
    // 如果没有head或超过最大容量,那么返回false,表示分配失败
    if (head == null || normCapacity > maxCapacity) {
        return false;
    }

    // 遍历chunklist
    for (PoolChunk<T> cur = head;;) {
        // 去chunk中申请空间
        long handle = cur.allocate(normCapacity);
        // 如果handle<0, 说明申请失败, 继续滚动到下一个chunk循环
        if (handle < 0) {
            cur = cur.next;
            if (cur == null) {
                return false;
            }
        } else {
            // 如果申请成功, 那么将内存空间与ByteBuf进行绑定
            cur.initBuf(buf, handle, reqCapacity);
            // 接着计算当前chunk的使用率,如果超过最大阈值,那么从当前list中删除,并移动到下个list
            // 也就是升舱处理
            if (cur.usage() >= maxUsage) {
                remove(cur);
                nextList.add(cur);
            }
            return true;
        }
    }
}

释放

boolean free(PoolChunk<T> chunk, long handle) {
    // 释放该chunk对应的handle持有的内存空间
    chunk.free(handle);
    // 如果小于最下,那么同理需要降级处理
    if (chunk.usage() < minUsage) {
        remove(chunk);
        // Move the PoolChunk down the PoolChunkList linked-list.
        return move0(chunk);
    }
    return true;
}

移动

private boolean move0(PoolChunk<T> chunk) {
    if (prevList == null) {
        assert chunk.usage() == 0;
        return false;
    }
    return prevList.move(chunk);
}
private boolean move(PoolChunk<T> chunk) {
    // 接着move0的上下文,这里已经到了prevlist, 判断是否满足该list的使用率条件
    assert chunk.usage() < maxUsage;
    
    // 如果还是低于该区的最小,那么继续降级
    if (chunk.usage() < minUsage) {
        return move0(chunk);
    }

    // 否则add到该list
    add0(chunk);
    return true;
}

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

推荐阅读更多精彩内容