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. ASP.NET Core MVC 502 bad gateway 超时如何处理

    在网页程序运行需要较长时间运行的时候,ASP.NET Core MVC会出现502 bad gateway请求超时情况.一般默认的超时时间都比较短,我们需要在 web.config 中配置一下.其中  ...

  2. JMX——以可视化形式管理与监控正在运行中的Java程序

    简单理解: MBean:管理的最小单元,一个MBean就是一个可以被监控的JavaBean. MBeanServer:一个池子,各个MBean都会注册到该池子中,并且该池子提供一系列的管理.监控API ...

  3. PCI_PCIe_miniPCIe规格说明

    PCI PCI是一种本地总线(并行),规格书名称:PCI Local Bus Specification.并行总线,插槽规格统一. PCI stands for Peripheral Componen ...

  4. Linux计划任务管理

    计划任务 类型:     一次性计划任务     周期性计划任务      一次性计划任务 前提:  atd服务必须运行 [root@wei init.d]# yum -y install at   ...

  5. ie和谷歌的兼容性问题

    1.表单的归类 ie下的表单元素在设置了disabled禁用属性之后,在ie下点击,仍然会有焦点.谷歌这是正常的没有焦点 解决方法:给表单元素设置增加属性 unselectable='on'  即可.

  6. 良心送分题(牛客挑战赛35E+虚树+最短路)

    目录 题目链接 题意 思路 代码 题目链接 传送门 题意 给你一棵树,然后把这棵树复制\(k\)次,然后再添加\(m\)条边,然后给你起点和终点,问你起点到终点的最短路. 思路 由于将树复制\(k\) ...

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

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

  8. HDU5126 stars(cdq分治)

    传送门 题意: 先有两种操作,插入和查询,插入操作则插入一个点\((x,y,z)\),查询操作给出两个点\((x_1,y_1,z_1),(x_2,y_2,z_2)\),回答满足\(x_1\leq x\ ...

  9. windows server 2008 安装MySQL 8.0 遇到报错 1055 - Expression #1 of ORDER BY clause is not in GROUP BY

    mysql安装参考教程:https://blog.csdn.net/qq_37350706/article/details/81707862 安装完毕后 执行sql语句 SELECT * FROM c ...

  10. 查询接口---flask+python+mysql

    环境准备 安装flask pip install  flask 项目结构如图 1.新建配置文件conf.py #!/usr/bin/python# -*- coding:utf-8 -*- impor ...