Tcmalloc源码分析(总括)

标签: 专题分析 | 发表时间:2011-10-21 13:55 | 作者:will.huang cpy
出处:http://www.tektalk.org

这段时间由于工作中涉及到内存疯长的事情,工作之余就对比分析了tcmalloc和ptmalloc的一些工作方式,关于ptmalloc代码的分析,已经有前人做了不少的工作,我这边主要讲述一下,我对tcmalloc的一点理解,写的比较随意,难免漏洞百出,有问题希望大家能够指正(email:huangjiangwei@gmail.om)开始写写还行,写到后面就不想写了,所以后面的章节代码多于评论和分析,自己都不想看下去了。
Tcmalloc通过preload或者直接动态链接的方式对malloc等内存分配和释放函数进行截获并提供服务。Tcmalloc提供接口主要涵盖malloc.h的接口。下面我将通过内存操作的基本流程,从分配开始到释放简单的分析tcmalloc的一些内部实现。

首先我们来看内存的分配。在tcmalloc中,内存分配malloc的入口为tc_malloc,new的入口为tc_new,相应的realloc,calloc,memalign,valloc等也有相应的入口。先简单描述一下tcmalloc的malloc过程和free过程。

Malloc过程

1、               Tcmalloc首先判断malloc的size是否大于kMaxSize(8u * kPageSize为32k),如果小于这个值,那么将size转换为想的obj class,然后从当前thread私有的cache中Allocate,转至第2步。如果请求的size大于kMaxSize那么跳至第10。

2、               首先判断当前的threadcache中obj calss对应的freelist中是否包含有空闲的obj,如果有直接pop出来,否则从CentralCache中拿,转下一步。

3、               CentralCache和ThreadCache之间obj的转移采用batch方式,每次转移固定数量的obj,这个数量通过Static::sizemap()->num_objects_to_move定义,当然在决定最终转移数量时还是需要不能超过ThreadCache相应list的maxlength。然后通过CentralCache对应freelist的RemoveRange函数将确定大小的obj转移出来,并通过对应list的PushRange函数将这些obj插入ThreadCache对应的freelist。

4、               CentralCache通过RemoveRange将特定数量的obj移出,CentralCache将连续的内存看做一个Span,Span是CentralCache管理内存的一个主要数据结构。而Span又被切分成N个统一大小的obj。每个CentralCache管理属于某个特定class的obj。在同一个CentralCache中所包含的obj大小符合特定calss的要求。CentralCache包含kNumTransferEntries(kNumClasses)个slots,这些slots用来存储正好包含num_objects_to_move个obj的Span。如果slots满了或者包含其他数量obj的Span将统一放入两个Span队列,empty和nonempty。这里的名字比较诡异empty函数存放的Span不是空闲的Span而是Span里面没有空闲obj的Span,nonempty则相反。

5、               因此在Allocate的过程中,首先判断需要Allocate的obj数量是不是正好符合num_objects_to_move,如果是而且CentralCache用来存放span的slots不为空,那么直接从slots里面拿,否则从nonempty队列中的Span拿。

6、               Nonempty队列存放了所有可用的Span,那么从头开始一个个拿,如果拿光了还是不能满足要求,那么只能通过向pageheap要求一个span,这个span的size由class_to_pages决定,然后再将这个Span切成obj返回给CentralCache。然后再次尝试从Span分配。

7、               Pageheap管理整个系统page级别的allocate,他通过两个数据结构管理所有的Span(free_数组和large_列表),free_数组存放size小于kMaxPages(1 << (20 – kPageShift)256)的Span,而large_列表存放大于等于kMaxPages的Span。PageHeap首先判断要求的pages是否大于等于kMaxPages,如果小于那么先从free数组中找,从要求大小的位置开始往后找,先找normal队列在找return对队列。如果在normal队列中找到且找到的Span状态为Span::ON_NORMAL_FREELIST,那么直接从里面切出需要的Span返回给CentralCache。如果在return队列中找到且找到的Span状态为Span:: ON_RETURNED_FREELIST那么直接从里面切出需要的Span返回给CentralCache。

8、               如果需要的size不符合上述要求或者在上述队列中没有找到那么将从large_队列中找。从large_队列中查找时,首先从normal队列入手,然后再从return队列找,他将找到size最符合且地址在空闲Span中最小的Span,然后切出来返回。

9、               如果large_队列中都没有找到合适的Span,那么将通过GrowHeap增长Heap的方式,通过TCMalloc_SystemAlloc向系统申请不小于1M的内存。并包装成Span,并插入heap中,然后再次进行分配。

10、           来到此处代表分配的内存是大于32k的,那么将向heap直接请求跳到第7步。

Free过程

1、             首先free根据提供的ptr指针,获得此ptr所在的页面,然后利用页面号,获取需要释放的obj的size,这个size首先从pagemap_cache_中拿,如果不在pagemap_cache_中那么直接通过页面好获取所在的span,如果span为NULL,那么直接报错返回,否则通过span获取需要释放的obj的size,并将size和page的关系放入pagemap_cache_。

2、             如果能够获取ptr所指obj的size,且本释放是在子线程内(可以获取threadcache),那么调用threadcache的Deallocate释放,转到一下步。否则利用InsertRange直接将obj插入Centralcache对应size的freelist。如果ptr所指的obj size为0,有可能是大的obj,直接从pageheap中拿的,那么跳第5步。

3、             Threadcache通过相应的size获得对应的freelist,然后通过push将obj链入freelist。如果插入之后的list太长将通过ReleaseToCentralCache将num_objects_to_move个obj返还给CentralCache,如果整个threadcache的size太大,那么将对整个free数组进行遍历,调用ReleaseToCentralCache函数。

4、             ReleaseToCentralCache将通过对应CentralCache的freelist调用InsertRange将需要返还给CentralCache的objs插入CentralCache。CentralCache判断所返还的obj数量是不是正好等于num_objects_to_move,如果是且slots_里面有空闲区,那么直接返还给slots_,否则将这些obj返回给相应的Span,如果本Span通过返还之后已经是有freeobj了,将此Span从empty_列表转移到nonempty_列表,如果此Span已经全部空闲了,没有还在被使用的obj,那么通过Static::pageheap()->Delete返还给pageheap。

5、             Pageheap通过Delete函数将返还回来Span的相关参数清零,并设置当前Span为ON_NORMAL_FREELIST。然后判断此Span能不能和前后相邻的Span合并,然后将Span插入相应size的normal队列,最后判断是否需要回收,如果需要,尝试回收至少一个页面,回收动作主要讲Span从normal列表转移到return列表,并调用TCMalloc_SystemRelease将页面标记为可回收的,然后通过系统调用madvise(reinterpret_cast<char*>(new_start), new_end – new_start,                   MADV_DONTNEED)将物理内存返回给系统,而保留虚拟内存。

相关 [tcmalloc 源码 分析] 推荐:

Tcmalloc源码分析(总括)

- cpy - 弯曲评论
Tcmalloc通过preload或者直接动态链接的方式对malloc等内存分配和释放函数进行截获并提供服务. Tcmalloc提供接口主要涵盖malloc.h的接口. 下面我将通过内存操作的基本流程,从分配开始到释放简单的分析tcmalloc的一些内部实现. 在tcmalloc中,内存分配malloc的入口为tc_malloc,new的入口为tc_new,相应的realloc,calloc,memalign,valloc等也有相应的入口.

TCmalloc对squid的性能的提升

- caoxg - 开心平淡对待每一天。热爱生活
           TCmalloc对squid的性能的提升一、简介:. 1、安装tcmalloc所需要的libunwind库 [32位系统不用安装]. *注意:据说加上’–with-large-files’ 选项时编译会出错. 4、配置好squid并启动squid.. squid+tcmalloc: 20 型号:Dell R410 硬盘:2*SAS/146G/15K 内存:16G CPU:16.

Redis源码分析-内存分配

- gOODiDEA - NoSQLFan
本文转载自Day Day Up博客,文章对Redis的内存分配封装zmalloc库进行了分析,描述了Redis在内存分配和使用统计方面的各种细节和技巧. 原文链接:blog.ddup.us. Redis中到处都会进行内存分配操作. 为了屏蔽不同平台之间的差异,以及统计内存占用量等,Redis对内存分配函数进行了一层封装,程序中统一使用zmalloc,zfree一系列函数,位于zmalloc.h,zmalloc.c文中.

Redis源码分析系列文章

- gOODiDEA - NoSQLFan
Redis 的源码只有2万来行,个人觉得是一个非常合适的学习Unix 环境下C语言编程的实例教材. 而读源码,也对了解Redis内部结构很有帮助. 下面推荐的几篇文章,来自阿里巴巴云计算运维部的 hoterran 同学的个人博客,分别对Redis几个重要流程的源码进行了分析研究,对了解Redis内部结构很有帮助.

nginx源码分析--GDB调试

- - CSDN博客架构设计推荐文章
利用gdb[i]调试nginx[ii]和利用gdb调试其它程序没有两样,不过nginx可以是daemon程序,也可以以多进程运行,因此利用gdb调试和平常会有些许不一样. 当然,我们可以选择将nginx设置为非daemon模式并以单进程运行,而这需做如下设置即可:. master_process off; 这是第一种情况:.

11个源码优化和分析的Java工具

- 山河之外 - 互联网的那点事...
enkatt Guhesan 分享了一些Java工具,帮助你优化代码以及检查源代码中的潜在问题. PMD能够扫描Java 源代码,查找类似以下的潜在问题:. 可能的bug——try/catch/finally/switch语句中返回空值. 死代码——未使用的局部变量、参数、私有方法. 不理想的代码——使用String/StringBuffer.

jquery源码分析之扩展函数 extend, $.extend

- - CSDN博客推荐文章
声明:本文为 斯人原创,全部为作者一一分析得之,有不对的地方望赐教. 本文地址: http://imsiren.com/archives/525. 好久没写jquery源码的内容了. jquery的发展有很大因素是因为它非常易于扩展,究其原因就得益于 extend函数. 该函数是一个扩展函数…说是一个扩展函数,其实就是一个浅拷贝和深拷贝的函数而已.

[原]Mahout 协同过滤 itemBase RecommenderJob源码分析

- -
Mahout支持2种 M/R 的jobs实现itemBase的协同过滤. 下面我们对RecommenderJob进行分析,版本是mahout-distribution-0.7. 源码包位置:org.apache.mahout.cf.taste.hadoop.item.RecommenderJob. RecommenderJob前几个阶段和ItemSimilarityJob是一样的,不过ItemSimilarityJob 计算出item的相似度矩阵就结束了,而RecommenderJob 会继续使用相似度矩阵,对每个user计算出应该推荐给他的top N 个items.

mahout源码分析之Decision Forecast 三部曲之一Describe

- - CSDN博客云计算推荐文章
Mahout版本:0.7,hadoop版本:1.0.4,jdk:1.7.0_25 64bit. Mahout中实现决策树算法的有两个(quick start),分别是 Partial Implementation和 Breiman Example,可以点击链接到相应的网页查看其官方实例. 其中Breiman Example是单机版的,而Partial Implementation是可以使用map-reduce模式的.