Netty源码分析第五章: ByteBuf

第五节: directArena分配缓冲区概述

上一小节简单分析了PooledByteBufAllocator中, 线程局部缓存和arean的相关逻辑, 这一小节简单分析下directArena分配缓冲区的相关过程

回到newDirectBuffer中:

protected ByteBuf newDirectBuffer(int initialCapacity, int maxCapacity) {
PoolThreadCache cache = threadCache.get();
PoolArena<ByteBuffer> directArena = cache.directArena;
ByteBuf buf;
if (directArena != null) {
buf = directArena.allocate(cache, initialCapacity, maxCapacity);
} else {
if (PlatformDependent.hasUnsafe()) {
buf = UnsafeByteBufUtil.newUnsafeDirectByteBuf(this, initialCapacity, maxCapacity);
} else {
buf = new UnpooledDirectByteBuf(this, initialCapacity, maxCapacity);
}
}
return toLeakAwareBuffer(buf);
}

获取了directArena对象之后, 通过allocate方法分配一个ByteBuf, 这里allocate方法是PoolArena类中的方法

跟到allocate方法中:

PooledByteBuf<T> allocate(PoolThreadCache cache, int reqCapacity, int maxCapacity) {
PooledByteBuf<T> buf = newByteBuf(maxCapacity);
allocate(cache, buf, reqCapacity);
return buf;
}

首先通过newByteBuf获得一个ByteBuf对象

再通过allocate方法进行分配, 这里要注意, 这里进行分配的时候是线程私有的directArena进行分配

我们跟到newByteBuf方法中

因为是directArena调用的newByteBuf, 所以这里会进入DirectArena类的newByteBuf中:

protected PooledByteBuf<ByteBuffer> newByteBuf(int maxCapacity) {
if (HAS_UNSAFE) {
return PooledUnsafeDirectByteBuf.newInstance(maxCapacity);
} else {
return PooledDirectByteBuf.newInstance(maxCapacity);
}
}

因为默认通常是有unsafe对象的, 所以这里会走到这一步中PooledUnsafeDirectByteBuf.newInstance(maxCapacity)

通过静态方法newInstance创建一个PooledUnsafeDirectByteBuf对象

跟到newInstance方法中:

static PooledUnsafeDirectByteBuf newInstance(int maxCapacity) {
PooledUnsafeDirectByteBuf buf = RECYCLER.get();
buf.reuse(maxCapacity);
return buf;
}

这里通过RECYCLER.get()这种方式拿到一个ByteBuf对象, RECYCLER其实是一个对象回收站, 这部分内容会在后面的内容中详细剖析, 这里我们只需要知道, 这种方式能从回收站中拿到一个对象, 如果回收站里没有相关对象, 则创建一个新

因为这里有可能是从回收站中拿出的一个对象, 所以通过reuse进行复用

跟到reuse方法中:

final void reuse(int maxCapacity) {
maxCapacity(maxCapacity);
setRefCnt(1);
setIndex0(0, 0);
discardMarks();
}

这里设置了的最大可扩容内存, 对象的引用数量, 读写指针位置都重置为0, 以及读写指针的位置标记也都重置为0

我们回到PoolArena的allocate方法中:

PooledByteBuf<T> allocate(PoolThreadCache cache, int reqCapacity, int maxCapacity) {
PooledByteBuf<T> buf = newByteBuf(maxCapacity);
allocate(cache, buf, reqCapacity);
return buf;
}

拿到了ByteBuf对象, 就可以通过allocate(cache, buf, reqCapacity)方法进行内存分配了

跟到allocate方法中:

private void allocate(PoolThreadCache cache, PooledByteBuf<T> buf, final int reqCapacity) {
//规格化
final int normCapacity = normalizeCapacity(reqCapacity);
if (isTinyOrSmall(normCapacity)) {
int tableIdx;
PoolSubpage<T>[] table;
//判断是不是tinty
boolean tiny = isTiny(normCapacity);
if (tiny) { // < 512
//缓存分配
if (cache.allocateTiny(this, buf, reqCapacity, normCapacity)) {
return;
}
//通过tinyIdx拿到tableIdx
tableIdx = tinyIdx(normCapacity);
//subpage的数组
table = tinySubpagePools;
} else {
if (cache.allocateSmall(this, buf, reqCapacity, normCapacity)) {
return;
}
tableIdx = smallIdx(normCapacity);
table = smallSubpagePools;
} //拿到对应的节点
final PoolSubpage<T> head = table[tableIdx]; synchronized (head) {
final PoolSubpage<T> s = head.next;
//默认情况下, head的next也是自身
if (s != head) {
assert s.doNotDestroy && s.elemSize == normCapacity;
long handle = s.allocate();
assert handle >= 0;
s.chunk.initBufWithSubpage(buf, handle, reqCapacity); if (tiny) {
allocationsTiny.increment();
} else {
allocationsSmall.increment();
}
return;
}
}
allocateNormal(buf, reqCapacity, normCapacity);
return;
}
if (normCapacity <= chunkSize) {
//首先在缓存上进行内存分配
if (cache.allocateNormal(this, buf, reqCapacity, normCapacity)) {
//分配成功, 返回
return;
}
//分配不成功, 做实际的内存分配
allocateNormal(buf, reqCapacity, normCapacity);
} else {
//大于这个值, 就不在缓存上分配
allocateHuge(buf, reqCapacity);
}
}

这里看起来逻辑比较长, 其实主要步骤分为两步

1.首先在缓存上进行分配, 对应步骤是:

cache.allocateTiny(this, buf, reqCapacity, normCapacity)

cache.allocateSmall(this, buf, reqCapacity, normCapacity)

cache.allocateNormal(this, buf, reqCapacity, normCapacity)

2.如果在缓存上分配不成功, 则实际分配一块内存, 对应步骤是

allocateNormal(buf, reqCapacity, normCapacity)

在这里对几种类型的内存进行介绍:

之前的小节我们介绍过, 缓冲区内存类型分为tiny, small, 和normal, 其实还有种不常见的类型叫做huge, 那么这几种类型的内存有什么区别呢, 实际上这几种类型是按照缓冲区初始化空间的范围进行区分的, 具体区分如下:

tiny类型对应的缓冲区范围为0-512B

small类型对应的缓冲区范围为512B-8K

normal类型对应的缓冲区范围为8K-16MB

huge类型对应缓冲区范围为大于16MB

简单介绍下有关范围的含义:

16MB对应一个chunk, netty是以chunk为单位向操作系统申请内存的

8k对应一个page, page是将chunk切分后的结果, 一个chunk对应2048个page

8k以下对应一个subpage, subpage是page的切分, 一个page可以切分多个subpage, 具体切分几个需要根据subpage的大小而定, 比如只要分配1k的缓冲区, 则会将page切分成8个subpage

以上就是directArena内存分配的大概流程和相关概念

上一节: PooledByteBufAllocator简述

下一节: 命中缓存的分配

Netty源码分析第5章(ByteBuf)---->第5节: directArena分配缓冲区概述的更多相关文章

  1. Netty源码分析第5章(ByteBuf)---->第4节: PooledByteBufAllocator简述

    Netty源码分析第五章: ByteBuf 第四节: PooledByteBufAllocator简述 上一小节简单介绍了ByteBufAllocator以及其子类UnPooledByteBufAll ...

  2. Netty源码分析第5章(ByteBuf)---->第6节: 命中缓存的分配

    Netty源码分析第6章: ByteBuf 第六节: 命中缓存的分配 上一小节简单分析了directArena内存分配大概流程, 知道其先命中缓存, 如果命中不到, 则区分配一款连续内存, 这一小节带 ...

  3. Netty源码分析第5章(ByteBuf)---->第7节: page级别的内存分配

    Netty源码分析第五章: ByteBuf 第六节: page级别的内存分配 前面小节我们剖析过命中缓存的内存分配逻辑, 前提是如果缓存中有数据, 那么缓存中没有数据, netty是如何开辟一块内存进 ...

  4. Netty源码分析第5章(ByteBuf)---->第10节: SocketChannel读取数据过程

    Netty源码分析第五章: ByteBuf 第十节: SocketChannel读取数据过程 我们第三章分析过客户端接入的流程, 这一小节带大家剖析客户端发送数据, Server读取数据的流程: 首先 ...

  5. Netty源码分析第5章(ByteBuf)---->第1节: AbstractByteBuf

    Netty源码分析第五章: ByteBuf 概述: 熟悉Nio的小伙伴应该对jdk底层byteBuffer不会陌生, 也就是字节缓冲区, 主要用于对网络底层io进行读写, 当channel中有数据时, ...

  6. Netty源码分析第5章(ByteBuf)---->第2节: ByteBuf的分类

    Netty源码分析第五章: ByteBuf 第二节: ByteBuf的分类 上一小节简单介绍了AbstractByteBuf这个抽象类, 这一小节对其子类的分类做一个简单的介绍 ByteBuf根据不同 ...

  7. Netty源码分析第5章(ByteBuf)---->第3节: 缓冲区分配器

    Netty源码分析第五章: ByteBuf 第三节: 缓冲区分配器 缓冲区分配器, 顾明思议就是分配缓冲区的工具, 在netty中, 缓冲区分配器的顶级抽象是接口ByteBufAllocator, 里 ...

  8. Netty源码分析第5章(ByteBuf)---->第8节: subPage级别的内存分配

    Netty源码分析第五章: ByteBuf 第八节: subPage级别的内存分配 上一小节我们剖析了page级别的内存分配逻辑, 这一小节带大家剖析有关subPage级别的内存分配 通过之前的学习我 ...

  9. Netty源码分析第5章(ByteBuf)---->第9节: ByteBuf回收

    Netty源码分析第五章: ByteBuf 第九节: ByteBuf回收 之前的章节我们提到过, 堆外内存是不受jvm垃圾回收机制控制的, 所以我们分配一块堆外内存进行ByteBuf操作时, 使用完毕 ...

随机推荐

  1. Spring-IOC MethodInvokingFactoryBean 类源码解析

    MethodInvokingFactoryBean MethodInvokingFactoryBean的作用是,通过定义类和它的方法,然后生成的bean是这个方法的返回值,即可以注入方法返回值. Me ...

  2. Spring Boot中使用EhCache实现缓存支持

     SpringBoot提供数据缓存功能的支持,提供了一系列的自动化配置,使我们可以非常方便的使用缓存.,相信非常多人已经用过cache了.因为数据库的IO瓶颈.一般情况下我们都会引入非常多的缓存策略, ...

  3. NYOJ 36 LCS(最长公共子序列)

    题目链接: http://acm.nyist.edu.cn/JudgeOnline/problem.php?pid=36 最长公共子序列 时间限制:3000 ms  |  内存限制:65535 KB ...

  4. 提取win10默认锁屏壁纸

    win10锁屏设置为windows聚焦时锁屏会有好看的图片出现.想让一张好看的图片一直使用,就去提取出来然后设置一下. 找到C盘:用户目录下 找到你的主机名文件夹: 在查看的选项栏中将隐藏文件夹显示: ...

  5. Kafka集群配置

    kafka_2.11-0.9.0.1.tgz 1.进入项目前的目录 cd /home/dongshanxia mkdir kafka #创建项目目录 cd kafka #进入项目目录 mkdir ka ...

  6. #leetcode刷题之路34-在排序数组中查找元素的第一个和最后一个位置

    给定一个按照升序排列的整数数组 nums,和一个目标值 target.找出给定目标值在数组中的开始位置和结束位置.你的算法时间复杂度必须是 O(log n) 级别.如果数组中不存在目标值,返回 [-1 ...

  7. Linux学习笔记(第十章)

    vim程序编辑器 vim特点: vim三种模式: 一般模式:打开文档就直接进入编辑模式 -可进行删除,复制等,无法直接编辑文档 编辑模式:按下[i,I,o,O,A,R,r]等字母才会进入编辑模式,按E ...

  8. 关于java8(Stream)的一些用法

    如果要处理int[] 转换成 List<Integer>这种形式的,可以用下面这个方法: List<Integer> orgIds = Arrays.stream(reqVo. ...

  9. mac 下安装php7.1 memcache扩展

    1.下载memcache源代码文件 https://github.com/websupport-sk/pecl-memcache/archive/php7.zip 文件夹名为:pecl-memcach ...

  10. angularjs animation

    http://augus.github.io/ngAnimate/ http://www.nganimate.org/angularjs/ng-repeat/move http://codepen.i ...