SizeMap

tcmalloc通过classid将不同的小对象映射到不同的对象桶中,sizemap记录了一些对象大小和对象class的映射以及反向映射,除此之外,还记录了一些ThreadCache与CentralCache层交互的时候批量处理的一些数据。
class_to_size_[kClassSizesMax]数组记录每个class中存储的对象大小
class_to_pages_[kClassSizesMax]数组记录当centraol cache向page map申请内存时一次申请的page数量
num_objects_to_move_[kClassSizesMax]数组记录每个线程与central cache层申请和释放时批量处理的对象个数
 

ThreadCache

顾名思义,每个线程一份内存Cache,线程在分配和释放内存的时候,首先从ThreadCache中分配和释放,没有锁争用开销,非常高效。
最重要的数据结构,就是FreeList      list_[kClassSizesMax],每个class一个桶,每个桶中是一个FreeList单链表,将所有该class的对象都链接起来。另外还有一个ThreadCache的prev和next指针,主要用来做数据统计用。
tcmalloc中所有的链表都是上一个对象的头部4字节或8字节中存储下一个对象的地址,如下所示:

CentralCache

CentralCache是所有线程共同使用的公共小对象池。CentralCache由一些CentralFreeList组成,每个CentralFreeList管理一个class的小对象,线程在访问CentralCache的时候需要加锁,锁的粒度是每个对象桶CentralFreeList。
CetralFreeList的核心数据结构是两个Span链表,一个叫empty_,另一个叫nonempty_。empty_链表中是其所有object都已经分配完毕的span,nonempty_链表中是部分分配或未分配object的span。由于ThreadCache与CentralCache每次批发的objects数量是恒定的(由上文中提到的num_objects_to_move_来确定),而对span的操作相对来说比较耗时,所以CentralFreeList中加入了名叫tc_slots_的cache,将其作为span的一个cache,每次分配内存和回收内存的时候,如果分配和回收的内存刚好是num_objects_to_move_中相应的数量,则优先走tc_slots_。每次分配的时候,优先从tc_slots_中寻找特定数量的objects,每次回收内存的时候,则优先将objects回收到tc_slots_中。
 

PageHeap

PageHeap是page级别的allocator,它的主要职责就是管理page,上文中提到的span,就是若干连续page组成的数据结构。
PageHeap的核心数据结构有:
1)名为pagemap_的PageMap,它是用来映射Page地址PageID和Span的。这是一个三层的radix_tree,提供的最主要的接口有两个,一是名为set的设置PageID和Span的映射,另一个是名为get的通过PageID获取Span。
2)名为free_[kMaxPages]的一个SpanList数据,kMaxPages在4k页的系统中是255,所以free_中有1个Page到255个Page的所有规格,span可以按照任意大小进行拆分和组合。
3)名为large_的SpanList,它用来存放大于kMaxPages的超大Page。
空闲span的伙伴系统为上层提供span的分配与回收。当需要的span没有空闲时,可以把更大尺寸的span拆小(如果大的span都没有了,则需要重新找kernel分配);当span回收时,又需要判断相邻的span是否空闲,以便将它们组合。判断相邻span还是要用到radix tree,radix tree就像一个大数组,很容易取到当前span前后相邻的span。
在向tcmalloc申请n个page的Span的时候,优先从free_数组的第n个下标开始寻找,如果free_[n]链表非空,就从这个链表中摘取一个元素,否则,从第n+1个下标开始寻找第一个空闲span,找到后将它切分成n个Page和K个Page,n个Page返回给应用,K个Page插入到free_[k]链表中,如果依然找不到空闲Span,就向操作系统申请一大块内存再分配。
 
每个SpanList由两个Span链表组成,一个是normal,一个是returned。normal链表是正常的空闲链表,returned链表是将要归还给操作系统的Span组成的空闲链表。在使用MallocExtension::GetStats打印出来tcmalloc的内存占用信息中,free_bytes统计来自normal链表,unmapped_bytes统计来自returned链表。
 
 

内存的分配过程

根据请求size判断是大块内存还是小块内存(256KB为边界,这个信息通过查询SizeMap表获得)
1,小块内存
     1), 通过size从SizeMap表中查到改请求对应的classid
     2), 从当前线程所在的ThreadCache的list_[classid]空闲链表中分配,如果分配成功则返回
     3), 尝试从CentralCache.list[class]空闲链表中一次性批发一批空闲object,返回一个给用户,其余的加入到ThreadCache.list_[class]空闲链表中。
     CentralFreeList中申请一批空闲object的申请顺序是:
     1) 优先从tc_slots中获取该数量的objects,tc_slots是一批空闲object的Cache,获取到则返回
     2) 从nonempty_链表中获取一个span,从该span中截取该数量的一批Objects,如果截取完毕之后span内的objects都用完了,那么将这个span插入到empty_链表中,否则增加span的引用计数,增加的数量是object的数量,获取到足够数量则返回
     3) 从nonempty_链表中尝试获取足量objects
     4) 向PageHeap申请若干个Page作为一个span,然后从span中截取足量的Objects,将截取完毕之后的span加入到nonempty_链表中
     PageHeap中申请n个Page的顺序是:
     1) 从PageHeap的free_[n]开始寻找空闲Span,首先尝试从free_[n].normal链表中寻找一个空闲Span,找到后从这个Span中截取n个Page返回,剩下的Page放入到对应的free_[x]链表中; 如果normal中获取不到,那么从free_[n].returned链表中寻找一个空闲的Span,然后做切分的操作。free[n]中找不到,从free[n+1]开始找,直到free[kMaxPages]
     2) 从large_链表中寻找,依然是先normal链表,再returned链表,找到后做切分的操作
     3) 尝试从操作系统申请一块至少kMaxPages大小的内存,继续1) 2)中的步骤
2,大块内存
     大块内存首先计算出需要多少个Page,然后从PageHeap中申请n个Page,流程同上。
 

内存的回收流程

    1,通过ptr的地址找到它所在的Page。对于4k Page的系统来说,每个地址除以4k就是它对应的PageId。
     2,通过PageId在pageheap中找到它对应的Span数据结构,从Span中获取到object所属的class。
    3,如果上面的Span中记录的class是0,说明这是一块大内存,直接调用PageHeap.Delete(Span*)来释放这块大内存;否则是一块小内存,小内存的回收:
        3.1 将这块内存插入到ThreadCache.freeList[cl]链表中,如果发现插入后的总长度大于了max_length,则尝试将若干个objects集体回收到centralCache中。
        3.2 尝试将这些Objects放入centralCache[cl]的FreeList中,如果发现centrailCache[cl]的FreeList已经有足够多的元素,则将这些objects集体回收到span中(通过调用ReleaseListToSpans(start)实现)。
        3.3 通过start地址找到pageId,然后在pageheap中通过PageId找到这些objects所属的span,将这些对象都插入到span->objects列表中。这一步中如果发现这个span上的objects都没有使用者了,就调用PageHeap.Delete(Span*)来释放这个Span。
 
     4, 释放Span:
        4.1 将这个span尝试放入到normal_freelist中。这个过程中,会尝试将pageheap中与此span内存地址相邻的span做合并,之后将合并后的span插入到free_[span->length]链表(小于128个page)或large_链表(大于128个page)中
        4.2 调用PageHeap::IncrementalScavenge(span->length)尝试去归还一部分Span给系统,并将这个Span插入到free_[span->length]链表或者large_链表的returned队列中。
        TCMalloc调用内存释放的接口TCMalloc_SystemRelease,它对应的系统调用的接口是madvise(),建议系统的行为是MADV_FREE,而MADV_FREE则将这些页标识为延迟回收。当内核内存紧张时,这些页将会被优先回收,如果应用程序在页回收后又再次访问,内核将会返回一个新的并设置为0的页。而如果内核内存充裕时,标识为MADV_FREE的页会仍然存在,后续的访问会清掉延迟释放的标志位并正常读取原来的数据,因此应用程序不检查页的数据,就无法知道页的数据是否已经被丢弃。
      因为 Linux 不支持 MADV_FREE,所以使用了 MADV_DONTNEED。使用 MADV_DONTNEED调用 madvise,告诉内核这段内存今后"很可能"用不到了,其映射的物理内存尽管拿去好了,因此,TCMalloc_SystemRelease 只是告诉内核,物理内存可以回收以做它用,但虚拟空间还留着,下次访问时会产生缺页中断,这个缺页中断会触发重新申请物理内存的操作。
      因此Span returned队列中的内存还是可以重新被上层模块申请使用的。

TCMalloc - 基本流程的更多相关文章

  1. redis采用tcmalloc导致无法释放内存的问题

    from:http://wangneng-168.iteye.com/blog/2100379 redis使用tcmalloc管理内存,当删除了redis的key后,通过redis的info命令查看内 ...

  2. 内存优化总结:ptmalloc、tcmalloc和jemalloc(转)

    转载于:http://www.cnhalo.net/2016/06/13/memory-optimize/ 概述 需求 系统的物理内存是有限的,而对内存的需求是变化的, 程序的动态性越强,内存管理就越 ...

  3. ptmalloc、tcmalloc和jemalloc

    内存优化总结:ptmalloc.tcmalloc和jemalloc 转载 2017年09月05日 18:57:12 3674 转载于:http://www.cnhalo.net/2016/06/13/ ...

  4. 内存优化总结:ptmalloc、tcmalloc和jemalloc

    概述 需求 系统的物理内存是有限的,而对内存的需求是变化的, 程序的动态性越强,内存管理就越重要,选择合适的内存管理算法会带来明显的性能提升.比如nginx, 它在每个连接accept后会malloc ...

  5. Linux下服务器端开发流程及相关工具介绍(C++)

    去年刚毕业来公司后,做为新人,发现很多东西都没有文档,各种工具和地址都是口口相传的,而且很多时候都是不知道有哪些工具可以使用,所以当时就想把自己接触到的这些东西记录下来,为后来者提供参考,相当于一个路 ...

  6. 基于netty http协议栈的轻量级流程控制组件的实现

    今儿个是冬至,所谓“冬大过年”,公司也应景五点钟就放大伙儿回家吃饺子喝羊肉汤了,而我本着极高的职业素养依然坚持留在公司(实则因为没饺子吃没羊肉汤喝,只能呆公司吃食堂……).趁着这一个多小时的时间,想跟 ...

  7. 通过重建Hosting系统理解HTTP请求在ASP.NET Core管道中的处理流程[下]:管道是如何构建起来的?

    在<中篇>中,我们对管道的构成以及它对请求的处理流程进行了详细介绍,接下来我们需要了解的是这样一个管道是如何被构建起来的.总的来说,管道由一个服务器和一个HttpApplication构成 ...

  8. nginx+iis+redis+Task.MainForm构建分布式架构 之 (redis存储分布式共享的session及共享session运作流程)

    本次要分享的是利用windows+nginx+iis+redis+Task.MainForm组建分布式架构,上一篇分享文章制作是在windows上使用的nginx,一般正式发布的时候是在linux来配 ...

  9. 8、Struts2 运行流程分析

    1.流程分析: 请求发送给 StrutsPrepareAndExecuteFilter StrutsPrepareAndExecuteFilter 询问 ActionMapper: 该请求是否是一个 ...

随机推荐

  1. Qt Creator清除最近工程历史信息

    Qt Creator清除最近工程历史信息 随着不断打开和关闭qt工程,欢迎->Projects->Recent projects下的历史工程信息越来越多,是该清理一下了,强迫症会追求干净一 ...

  2. 破解压缩包的几种方式(zip伪加密 爆破 CRC32碰撞 已知明文攻击)

    zip伪加密 zip文件是由3部分组成,详见文末 压缩源文件数据区+压缩源文件目录区+压缩源文件目录结束标志 在压缩源文件数据区有个2字节的 全局方式位标记 ,在压缩源文件目录区也有个2字节的 全局方 ...

  3. 十、lambda表达式、内置函数之filter、map、reduce

    lambda表达式   学习条件运算时,对于简单的 if else 语句,可以使用三元运算来表示,即: # 普通条件语句 == : name = 'wupeiqi' else: name = 'ale ...

  4. 【转】Pandas学习笔记(四)处理丢失值

    Pandas学习笔记系列: Pandas学习笔记(一)基本介绍 Pandas学习笔记(二)选择数据 Pandas学习笔记(三)修改&添加值 Pandas学习笔记(四)处理丢失值 Pandas学 ...

  5. BSGS算法(大小步算法)

    $BSGS$ 算法 $Baby\ Steps\ Giant\ Steps$. 致力于解决给定两个互质的数 $a,\ p$ 求一个最小的非负整数 $x$ 使得 $a^x\equiv b(mod\ p)$ ...

  6. mybatis 模糊查询 mapper.xml的写法

    1. sql中字符串拼接 SELECT * FROM tableName WHERE name LIKE CONCAT(CONCAT('%', #{text}), '%'); 2. 使用 ${...} ...

  7. java Atomic compareAndSet部分原理分析

    以AtomicLong的compareAndSet方法举例.先说结论:如果CPU支持,则基于CPU指令(CMPXCHG8)实现:否则使用ObjectLocker锁实现. 分析过程如下: 该方法在jdk ...

  8. AcWing 38. 二叉树的镜像

    习题地址 https://www.acwing.com/solution/acwing/content/2922/ 题目描述输入一个二叉树,将它变换为它的镜像. 样例 输入树: / \ / \ / \ ...

  9. 小学四则运算口算练习app---No.2

    经过昨天的了解,虽然还是很懵,总要下手摸到鼠标来写第一个页面! 这是一开始设置出体数目和时间的页面,使用者根据提示进行相关设置即可! 代码如下: <?xml version="1.0& ...

  10. 安装Vyos

      Vyos是一个开源的网络操作系统,基于Debian,相对于ROS需要购买license,Vyos就更加开放的多. 下载Vyos wget http://vyos.hecint.com/iso/re ...