打算一边学习tcmalloc的源码一边写总结文章。先从转述TCMalloc的一篇官方文档开始(TCMalloc : Thread-Caching Malloc)。

为什么用TCMalloc

TCMalloc比glibc 2.3 的malloc(是一个单独的库叫做ptmalloc2)和其他测试过的malloc都要快。TCMalloc致力于减少多线程对锁的竞争,在获取小的内存对象时候基本上不需要锁,在获取大的内存对象时候,TCMalloc使用了细粒度的高效率的spinlocks。ptmalloc2当然也有做减少线程间对锁进行竞争的努力,它每一个线程都有一个areans,彼此互不关联,这样显然有个问题就是一个线程释放的内存另外一个线程无法使用,只能新申请内存,如此下去,内存只会暴涨了。TCMalloc另一个优点是非常高效的空间利用率,例如在分配N个8 bytes的objects内存的时候,大约最多只会有8N *0.01 bytes(其实是8N / 7),后面源码分析会解释)的overhead,相比之下ptmalloc则是每一个object用了4bytes头,overhead总共达到了N*4 bytes。

用法(略)

原理概述

TCMalloc给每一个线程也分配一个线程本地的cache。然后小对象的分配都是从这个cache中获取。而线程本地的cache又是按需从所有线程共享的一个central heap中获取的。周期的垃圾回收可以把部分内存从线程本地的cache挪回central heap,达到内存分配在多个线程中按需调度的目的。

TCMalloc对待大的内存(文章说大于32K,但其实看代码应该是大于256K的内存)的分配是直接从central heap中使用页层级的分配器(一页内存就是按照 4K 对齐的内存区域)分配的,也就是一个大的对象地址总是按页对齐横跨多个页。

在central heap中内存也是按页向系统请求分配的,而这些页内存通常会被裁剪成许多小对象,例如一页4K的内存能够被裁减成32个大小为128字节的objects。

小对象的分配

每一个小对象对应了一个大约有60项的map中的一项。每一项代表了包含这个size的内存,程序中用了一个链表把该size的可用的object串起来,总的来说这个map就是线程本地cahche。map中的表项按照尺寸划分的,对于前面小一点的size是每8个字节增加一项,大一点的就是16字节,具体的哪一个size对应哪一项,程序中有注释如下:

//
// Examples:
//   Size       Expression                      Index
//   -------------------------------------------------------
//   0          (0 + 7) / 8                     0
//   1          (1 + 7) / 8                     1
//   ...
//   1024       (1024 + 7) / 8                  128
//   1025       (1025 + 127 + (120<<7)) / 128   129
//   ...
//   32768      (32768 + 127 + (120<<7)) / 128  376

线程本地的cache每一个size对应的Index中放置的是一个linklist,叫做free list,把可用的object内存串起来了。

分配小对象的过程是:(1)找到要分配的size对应cache表中的那一项;(2)在线程本地的cache中即size对应的free list中寻找可用内存;(2)假如free list不是空的就取出第一个object返回,这个过程不需要锁;

假如free list是空的,则进行以下步骤:(1)从对应size项的central free list中(这个是所有线程共享的)提取一些对象内存;(2)提取到的一些对象内存放到对应的线程本地free list项中;(3)从新提取的一些对象中获取一个对象作为返回值。

假如central free list中可用内存也是空的,则(1)从central page allocator中分配出一些页内存;(2)把这些页内存划分成一个一个object内存;(3)把这些新的objects挂在对应的central free list上;(4)参照之前,从其中移动一些内存到线程本地free list中。

大对象的分配

大的内存分配是按页直接从central heap page获取的。central heap page也是一个free list数组,第n项代表数个n页内存串起来的链表。

      • 分配k pages的内存就直接从数组的第k项的free list找可用内存,如果是空的就从下一项找,一直等到找到再返回,如果这样还是找不到,就要找系统去申请内存了(sbrk,mmap,。。。)。假如在一个大于k的free list中找到了空闲内存,多出来的内存要再插到数组合适的位置中去。
    • Spans

      一个 Span 对象代表一些连续页内存,具体来说就是它在central heap page中是某一个free list中的节点,也是central free list某一个free list中的节点。在后者中,他还会把自己代表的页内存划分成对应size的一个一个objects,并用链表方式串起来,并等待线程本地cache去获取。

      TCMalloc还有一个所有线程共享的数据结构就是一个map(文中叫做 central array),记录某一个页内存的页号具体对应哪一个span:

      一个32位的系统共有4GB的虚拟地址空间,总共对应2^20 4K页,所以这个map最多占用4M的内存,看起来还可以接受。但是在64位的系统上就不行了,所以64位系统我们使用radix tree来代替数组实现这样的map。

    • 内存释放

      当释放一个内存对象的时候,我们算出他所在页的页号,然后在central array中找到该页号对应的Span,Span可以获取对应的sizeclass,然后可以知道是小内存对象还是大内存对象。假如是小块内存对象,把其插入线程本地的合适的free list中,假如线程本地cache超过了一个指定的大小(默认是2MB),垃圾收集器这时候会启动起来把没有在使用的objects从线程本地cache挪动到central free lists中。

      假如是大块的内存对象,Span同时可以获取这个object占据的连续页内存,比如是占据了 [p,q]页,我们也会判断第p-1,q+1页,假如他们是空闲的,可以和[p,q]页合并,然后插入到central page heap的合适free list中。

管理小内存对象的Central Free Lists

Central Free Lists是一个Free list的链表数组,代表着每一个szie类别当前可用的内存,free list的每一个节点是Span,Span内部则又是一些objects构成的链表。

内存在线程本地的Cache和Central Free List之间的移动是以这些object为单位的,从Central Free list申请内存,会从中移动适量个数的object到线程本地Cache,线程本地Cache返还内存时,也会把这些objects放到Central Free list原来所在的Span中去。

当Span每分配出一个object就增加一个引用计数,每被返还一个object就释放一个引用计数,在返还时如果引用技术为0,即没有地方在引用这个Span了,这时就会把Span所占用的页直接释放回heap page。

线程本地Cache的垃圾回收

前面也已经提过,当一个线程本地的Cache其超过2MB后会被垃圾回收的。垃圾回收的这个阈值也会随着线程个数的增加而降低,因为这样可以减少应用程序对内存过度的使用。

垃圾回收时决定在一个free list中回收多少个内存对象是由每个size对应的free list的低水位线 L 控制的。L 记录了上次垃圾回收时这个free list的最小长度,每次回收 L/2 个内存对象。这种使用历史记录作为对未来预测的算法有比较好的效果,使得不同size的内存对象可以比较快速的在各个线程中交换使用。

TCMalloc源码学习(一)的更多相关文章

  1. TCMalloc源码学习(四)(小内存块释放)

    pagemap_和pagemap_cache_ PageHeap有两个map,pagemap_记录某一内存页对应哪一个span,显然可能多页对应一个span,pagemap_cache_记录某一内存页 ...

  2. TCMalloc源码学习(二)

    替换libc中的malloc free 不同平台替换方式不同. 基于unix的系统上的glibc,使用了weak alias的方式替换.具体来说是因为这些入口函数都被定义成了weak symbols, ...

  3. TCMalloc源码学习(三)(小块内存分配)

    线程本地cache 线程本地cache对应的是类 ThreadCache,每一个thread一个实例,初始化代码在static函数CreateCacheIfNecessary中, 在该线程第一次申请内 ...

  4. Java集合专题总结(1):HashMap 和 HashTable 源码学习和面试总结

    2017年的秋招彻底结束了,感觉Java上面的最常见的集合相关的问题就是hash--系列和一些常用并发集合和队列,堆等结合算法一起考察,不完全统计,本人经历:先后百度.唯品会.58同城.新浪微博.趣分 ...

  5. jQuery源码学习感想

    还记得去年(2015)九月份的时候,作为一个大四的学生去参加美团霸面,结果被美团技术总监教育了一番,那次问了我很多jQuery源码的知识点,以前虽然喜欢研究框架,但水平还不足够来研究jQuery源码, ...

  6. MVC系列——MVC源码学习:打造自己的MVC框架(四:了解神奇的视图引擎)

    前言:通过之前的三篇介绍,我们基本上完成了从请求发出到路由匹配.再到控制器的激活,再到Action的执行这些个过程.今天还是趁热打铁,将我们的View也来完善下,也让整个系列相对完整,博主不希望烂尾. ...

  7. MVC系列——MVC源码学习:打造自己的MVC框架(三:自定义路由规则)

    前言:上篇介绍了下自己的MVC框架前两个版本,经过两天的整理,版本三基本已经完成,今天还是发出来供大家参考和学习.虽然微软的Routing功能已经非常强大,完全没有必要再“重复造轮子”了,但博主还是觉 ...

  8. MVC系列——MVC源码学习:打造自己的MVC框架(二:附源码)

    前言:上篇介绍了下 MVC5 的核心原理,整篇文章比较偏理论,所以相对比较枯燥.今天就来根据上篇的理论一步一步进行实践,通过自己写的一个简易MVC框架逐步理解,相信通过这一篇的实践,你会对MVC有一个 ...

  9. MVC系列——MVC源码学习:打造自己的MVC框架(一:核心原理)

    前言:最近一段时间在学习MVC源码,说实话,研读源码真是一个痛苦的过程,好多晦涩的语法搞得人晕晕乎乎.这两天算是理解了一小部分,这里先记录下来,也给需要的园友一个参考,奈何博主技术有限,如有理解不妥之 ...

随机推荐

  1. git 清除本地git commit的内容

    由于我经常git add . , 然后再git commit -m "文字说明",这样有时候代码嵌套再另一个项目里面,就会把外面的项目一起提交了,导致提交的代码不是我想要的.小菜鸟 ...

  2. 如何优雅的将Object转换成List

    Main主函数中的 Object obj模拟了List对象.后续的代码首先判断obj是否是List类型,然后使用Class.cast做类型转换. 如果你想使用更方便的方法,可以直接调用下面的函数. p ...

  3. springcloud组件gateway断言(Predicate)

    Spring Cloud Gateway是SpringCloud的全新子项目,该项目基于Spring5.x.SpringBoot2.x技术版本进行编写,意在提供简单方便.可扩展的统一API路由管理方式 ...

  4. React 入门-写个 TodoList 实例

    React 是一个用于构建用户界面的 JavaScript 库,主要特点有: 声明式渲染:设计好数据和视图的关系,数据变化 React 自动渲染,不必亲自操作DOM 组件化:页面切分成多个小部件,通过 ...

  5. API企业级网关设计

    前言 摘自--https://github.com/aCoder2013/blog/issues/35 假设你正在开发一个电商网站,那么这里会涉及到很多后端的微服务,比如会员.商品.推荐服务等等. 那 ...

  6. 痞子衡嵌入式:MCUXpresso IDE下添加新路径下源文件进工程编译的方法

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是MCUXpresso IDE下添加新路径下源文件进工程编译的方法. 接着上篇文章 <MCUXpresso IDE下SDK工程导入与 ...

  7. 入门oj 5499: 讲话模式

    Description 每个人说话都有口头禅,现给出一个字符串,请求出其中出现次数最多的单词(不区分大小写). Input 输入一行,长度小于等于1048576的字符串输入至少包含一个字母或数字 Ou ...

  8. .net通过iTextSharp.pdf操作pdf文件实现查找关键字签字盖章

    之前这个事情都CA公司去做的,现在给客户做demo,要模拟一下签字盖章了,我们的业务PDF文件是动态生成的所以没法通过坐标定位,只能通过关键字查找定位了. 之前在网上看了许多通多通过查询关键字,然后图 ...

  9. js 中的 DOM 和 BOM

    BOM浏览器对象模型   概念:Browser Object Model   组成:   Window:浏览器窗口对象   Navigator:浏览器对象   screen:显示器屏幕对象   His ...

  10. linux based bottlerocket-os

    linux based bottlerocket-os 概要 aws开源,专注与运行容器的linux os 参看 https://github.com/bottlerocket-os