tiny内存分配

tiny内存分配流程:

  1. 如果申请的是tiny类型,会先从tiny缓存中尝试分配,如果缓存分配成功则返回

  2. 否则从tinySubpagePools中尝试分配

  3. 如果上面没有分配成功则使用allocateNormal进行分配

从缓存中分配

这里以启用了缓存为例来说明,使用到的缓存类是PoolThreadCache,缓存是通过队列实现的,一个队列中存储的内存大小都是相同的

// io.netty.buffer.PoolArena#allocate(io.netty.buffer.PoolThreadCache, io.netty.buffer.PooledByteBuf<T>, int)
// 这里的cache是PoolThreadCache
if (cache.allocateTiny(this, buf, reqCapacity, normCapacity)) {
// was able to allocate out of the cache so move on
return;
} boolean allocateTiny(PoolArena<?> area, PooledByteBuf<?> buf, int reqCapacity, int normCapacity) {
// 缓存维护了一个队列,这个队列中存储的内存块大小都相同
// 找到缓存之后,从队列中取出一个内存块用来初始化buffer
return allocate(cacheForTiny(area, normCapacity), buf, reqCapacity);
} // 查找缓存数组中缓存的内存
private MemoryRegionCache<?> cacheForTiny(PoolArena<?> area, int normCapacity) {
// 计算出申请内存位于缓存数组中的位置,即数组下标
int idx = PoolArena.tinyIdx(normCapacity);
if (area.isDirect()) {
// 使用直接内存的缓存
return cache(tinySubPageDirectCaches, idx);
}
// 使用堆内存的缓存
return cache(tinySubPageHeapCaches, idx);
} static int tinyIdx(int normCapacity) {
// 由于tiny缓存数组大小是32,依次对应的内存大小是16、32...,512,所以数组的下标应该是申请内存的大小除以16
// normCapacity = normCapacity / 16
return normCapacity >>> 4;
}

从缓存中分配内存的过程

  1. 寻找缓存tinySubPageDirectCaches
  2. 使用缓存中的chunk初始化buf

关于tiny缓存的数据结构

// tiny缓存
// 是一个SubPageMemoryRegionCache数组,默认缓存数组长度:32,依次存储的内存大小是16、32、48...,512
io.netty.buffer.PoolThreadCache#tinySubPageDirectCaches // 初始化tiny缓存数组,数组大小是32
// static final int numTinySubpagePools = 512 >>> 4;
tinySubPageDirectCaches = createSubPageCaches(
tinyCacheSize, PoolArena.numTinySubpagePools, SizeClass.Tiny); // 初始化tiny和small缓存数组
private static <T> MemoryRegionCache<T>[] createSubPageCaches(
int cacheSize, int numCaches, SizeClass sizeClass) {
if (cacheSize > 0) {
@SuppressWarnings("unchecked")
MemoryRegionCache<T>[] cache = new MemoryRegionCache[numCaches];
for (int i = 0; i < cache.length; i++) {
// TODO: maybe use cacheSize / cache.length
cache[i] = new SubPageMemoryRegionCache<T>(cacheSize, sizeClass);
}
return cache;
} else {
return null;
}
}

缓存使用队列实现,一个队列最大元素个数

DEFAULT_TINY_CACHE_SIZE = SystemPropertyUtil.getInt("io.netty.allocator.tinyCacheSize", 512);

从tinySubpagePools中分配

上面缓存中如果没有分配到内存的话,会向内存池tinySubpagePools申请,主要逻辑是:

  1. 计算tinySubpagePools数组的index,右移4,除以16(该数组长度是512>>>4)
  2. 取出index出的subpage,也就是这个index处head
  3. 从head.next查找合适的内存
  4. 找到可用内存后使用io.netty.buffer.PoolChunk#initBufWithSubpage(io.netty.buffer.PooledByteBuf, long, int)初始化buffer

前面已经介绍过tinySubpagePools,是一个数组,数组大小是32,每个元素是一个PoolSubpage,PoolSubpage本身是一个链表,所以要在这个里面查找可用内存,先要计算出数组下表,然后找到该位置的PoolSubpage,取出这个链表的头,然后分配内存。关键代码如下

// io.netty.buffer.PoolArena#allocate(io.netty.buffer.PoolThreadCache, io.netty.buffer.PooledByteBuf<T>, int)
private void allocate(PoolThreadCache cache, PooledByteBuf<T> buf, final int reqCapacity) {
final int normCapacity = normalizeCapacity(reqCapacity);
if (isTinyOrSmall(normCapacity)) { // capacity < pageSize
int tableIdx;
PoolSubpage<T>[] table;
boolean tiny = isTiny(normCapacity);
if (tiny) { // < 512
// 省略中间代码...
tableIdx = tinyIdx(normCapacity);
table = tinySubpagePools;
} else {
// 省略中间代码...
} final PoolSubpage<T> head = table[tableIdx]; /**
* Synchronize on the head. This is needed as {@link PoolChunk#allocateSubpage(int)} and
* {@link PoolChunk#free(long)} may modify the doubly linked list as well.
*/
synchronized (head) {
final PoolSubpage<T> s = head.next;
// 空链表的话,head指向自己
if (s != head) {
assert s.doNotDestroy && s.elemSize == normCapacity;
long handle = s.allocate();
assert handle >= 0;
s.chunk.initBufWithSubpage(buf, handle, reqCapacity);
incTinySmallAllocation(tiny);
return;
}
}
// 省略中间代码...
}

为什么上面没有没有判断head.next是否有可用的内存呢?

tinySubpagePools里面存储的是已经被分配过部分内存的PoolSubpage,PoolSubpage本身是一个链表,如果链表中除了head外有其他PoolSubpage,那么这个subpage一定有可以用的内存块,因为在PoolSubpage.allocate的时候,如果发现没有可用的内存块了,会将subpage从链表中移除。

按照normal的方式分配

如果上面两种方法都没有分配到内存,则调用allocateNormal方法来分配内存,allocateNormal获取内存的方法前面已经说过了,这里不再赘述。

small内存分配

small的分配过程和tiny内存的分配过程几乎一致

small内存分配流程:

  1. 如果申请的是tiny类型,会先从small缓存中尝试分配,如果缓存分配成功则返回

  2. 否则从smallSubpagePools中尝试分配

  3. 如果上面没有分配成功则使用allocateNormal进行分配

small内存的获取过程和tiny的方式类似,可以对照学习,这里不再详述。

Netty源码—六、tiny、small内存分配的更多相关文章

  1. [图解tensorflow源码] [转载] tensorflow设备内存分配算法解析 (BFC算法)

    转载自 http://weibo.com/p/1001603980563068394770   @ICT_吴林阳 tensorflow设备内存管理模块实现了一个best-fit with coales ...

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

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

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

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

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

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

  5. Netty源码分析第5章(ByteBuf)---->第5节: directArena分配缓冲区概述

    Netty源码分析第五章: ByteBuf 第五节: directArena分配缓冲区概述 上一小节简单分析了PooledByteBufAllocator中, 线程局部缓存和arean的相关逻辑, 这 ...

  6. Netty源码分析--内存模型(上)(十一)

    前两节我们分别看了FastThreadLocal和ThreadLocal的源码分析,并且在第八节的时候讲到了处理一个客户端的接入请求,一个客户端是接入进来的,是怎么注册到多路复用器上的.那么这一节我们 ...

  7. netty源码解析(4.0)-27 ByteBuf内存池:PoolArena-PoolThreadCache

    前面两章分析的PoolChunk和PoolSubpage,从功能上来说已经可以直接拿来用了.但直接使用这个两个类管理内存在高频分配/释放内存场景下会有性能问题,PoolChunk分配内存时算法复杂度最 ...

  8. Netty 源码解析(六): Channel 的 register 操作

    原创申明:本文由公众号[猿灯塔]原创,转载请说明出处标注 今天是猿灯塔“365篇原创计划”第六篇. 接下来的时间灯塔君持续更新Netty系列一共九篇   Netty 源码解析(一 ):开始 Netty ...

  9. Netty源码分析(前言, 概述及目录)

    Netty源码分析(完整版) 前言 前段时间公司准备改造redis的客户端, 原生的客户端是阻塞式链接, 并且链接池初始化的链接数并不高, 高并发场景会有获取不到连接的尴尬, 所以考虑了用netty长 ...

随机推荐

  1. css实现背景颜色渐变效果

    自己学的一些渐变:background:linear-gradient(90deg,#4EB9E7 -50%,#3691BE 50%,#2E83B0 100%); IE没有效果 详细访问: http: ...

  2. composer安装laravel指定版本

    版权声明:本文为博主原创文章,未经博主允许不得转载. http://blog.csdn.net/qq_38125058/article/details/79126051 首先安装composer,附安 ...

  3. mysql 基础语法掌握~ This is just the beginning.

    create database 数据库名; drop database 数据库名; use 数据库名; create table table_name ( column_name, column_ty ...

  4. webpack bug及解决方案

    1.webpack打包后z-index失效 解决方案:z-index设置成行内样式,例如:root.style.cssText = 'z-index:100000 !important;';

  5. jquery-layer弹框在火狐浏览器中弹框不显示的问题

    在使用layer控件设置弹框时, 谷歌浏览器中能正常弹出, 显示在页面中央位置. 而在火狐浏览器中, 弹框只显示标题, 并且弹框内容不显示. 在火狐浏览器中弹框的效果如下图红色方框中的弹出框所示, 但 ...

  6. java位 、字节 、字符的梳理

    1字节(byte)=8位(bit) char=2字节(这是因为char是Java中的保留字,Java用的是Unicode,所以char在Java中是16位即2个字节的.) 附: String str= ...

  7. iOS常用控件尺寸大集合

    元素控件 尺寸(pts) Window(含状态栏) 320 x 480 Status Bar的高度 20 Navigation Bar的高度 44 含Prompt的Navigation Bar的高度 ...

  8. 【redis 基础学习】(六)Redis HyperLogLog

    摘自:http://www.mayou18.com/detail/o6M0v9mi.html Redis  HyperLogLog 结构讲解 Redis 在 2.8.9 版本添加了 HyperLogL ...

  9. opencv利用hough概率变换拟合得到直线后,利用DDA算法得到直线上的像素点坐标

    图片霍夫变换拟合得到直线后,怎样获得直线上的像素点坐标? 这是我今天在图像处理学习中遇到的问题,霍夫变换采用的概率霍夫变换,所以拟合得到的直线信息其实是直线的两个端点的坐标,这样一个比较直接的思路就是 ...

  10. Linux内核编程、调试技巧小集

    1. 内核中通过lookup_symbol_name获取函数名称 内核中很多结构体成员是函数,有时可能比较复杂不知道具体使用哪一个函数.这是可以通过lookup_symbol_name来获取符号表名称 ...