TCmalloc学习

  • Post author:
  • Post category:其他



目录


TCmalloc简介


TCMalloc内部结构


TCMalloc 的工作模式


tcmalloc申请内存流程


tcmalloc释放内存流程


TCmalloc简介

TCMalloc 是 Google 自定义的 c 的 malloc () 和 c + + 操作符 new 的实现,用于 c 和 c + + 代码中的内存分配。TCMalloc 将 c 的 malloc () 和 c + + 操作符 new 的内部实现替换为 TCMalloc 的实现,开发者只需编译链接 TCMalloc 的静态库或动态库即可,无需改动任何与内存分配有关的代码。

TCMalloc 通常被用于提高内存分配的性能,实现了高效的内存管理。在与glibc中的内存分配器


ptmalloc2


作比较:

  • tcmalloc分配一次内存的时间更快
  • tcmalloc优化了对小对象的存储,需要更少的空间
  • tcmalloc特别在多线程方面做了优化,一方面是对于小对象(<32k)的分配基本不存在锁,内存资源竞争,另一方面是对大对象(>=32k)使用了细粒度、高效的自旋锁。
  • tcmalloc分配给本地线程的内存资源,在长时间空闲的情况下会被回收,供其他线程使用,提高了多线程时内存利用率,不会浪费内存,这一点ptmalloc2无法做到。

粒度解释:“粒度”表示的是精确程度问题。粗粒度角度描述一个系统,是关注系统中大的组件;细粒度角度描述一个系统是从组成大组件的小组件,或者更小组件的角度认识系统。

“粒度”此处是用来描述一个系统,或者对比多个系统的术语,它是一个相对的概念。

说明:glibc是

GNU

发布的libc库,即c

运行库

。glibc是

linux系统

中最底层的

api

,几乎其它任何运行库都会依赖于glibc。glibc除了封装

linux

操作系统所提供的

系统服务

外,它本身也提供了许多其它一些必要功能服务的实现。由于 glibc 囊括了几乎所有的

UNIX

通行的标准,可以想见其内容包罗万象。而就像其他的 UNIX 系统一样,其内含的档案群分散于系统的树状

目录结构

中,像一个支架一般撑起整个操作系统。在 GNU/Linux 系统中,其C函式库发展史点出了GNU/Linux 演进的几个重要里程碑,用 glibc 作为系统的C函式库,是GNU/Linux演进的一个重要里程碑。(参考于

glibc 简介: – the_tops – 博客园 (cnblogs.com)


TCMalloc内部结构

  • front-end :front-end 是一个缓存,它为应用程序提供内存的快速分配和释放。
  • middle-end :middle-end 负责为 front-end 填充或回收缓存。
  • back-end : back-end 负责从 OS 获取或释放内存。

TCMalloc 的工作模式

  1. tcmalloc会为每个线程分配本地缓存,小对象请求可以直接从本地缓存获取,如果没有空闲内存,则从central heap中一次性获取一连串小对象。
  2. tcmalloc对于小内存,按8的整数次倍分配,对于大内存,按4K的整数次倍分配。
  3. 当某个线程缓存中所有对象的总大小超过2MB的时候,会进行垃圾收集。垃圾收集阈值会自动根据线程数量的增加而减少,这样就不会因为程序有大量线程而过度浪费内存。

图1: Page和Span的关系

图2:通过PageMap实现Page到Span的映射\通过伙伴系统,实现了Span的分裂与合并

图3:每种规则的对象,都有一个CentralCache、向PageHeap申请Span,为用户分配对象


每种规则的对象都有一个独立的内存分配单元:



CentralCache



。在一个CentralCache内存,我们用链表把所有的Span组织起来,每次需要分配时就找一个Span从中分配一个Object;当没有空闲的Span时,就从PageHeap申请Span。


但是多线程情景,大家都在



CentralCache



分配资源,就会出现竞争。


每一个线程都有一个局部的



ThreadCache,



如果



ThreadCache



不够了,就在



CentralCache



集中分配,如果



CentralCache



中依然没有内存可分配,就在



PageHeap



中申请



Span



,如果



PageHeap



没有合适的



Page



可以申请



Span



,就在操作系统中申请。


释放内存时,

ThreadCache

遵循着批量释放的原则,当对象积累到一定程度就释放上一层

CentralCache

;当

CentralCache

发现一个

Span

的内存完全释放了,就将这个

Span

归还给

PageHeap



PageHeap

发现一批连续的

Page

都释放了,就归还给操作系统了。

tcmalloc申请内存流程

  1. 首先根据申请空间的大小从当前线程的可用内存块里面找(每个进程维护一组链表,每个链表代表一定大小的可用空间)
  2. .分配对象时,大的对象直接分配给Span,小的对象从Span中分配object
  3. . 如果没找到,就到central list里面查找(central list跟线程各自维护的list结构很像,为不同的size各自维护一组可用空间列表)
  4. 如果scentral list也没有找到,则计算分配size个字节需要分配多少page(变量:class_to_pages)
  5. 根据pagemap查找page对应的可用的span列表,如果找到了,则直接返回span,central list会将该span切割成合适的大小放入对应的列表中,然后交给thread cache
  6. 如果没有找到可用的span,则向OS直接申请,然后步骤同step 5。


page管理:


伙伴系统:合并相邻的page,减少外部碎片


内存申请:分裂Span


内存释放:合并Span

tcmalloc释放内存流程

1. 释放某个object

2. 找到该object所在的span

3. 如果该span中所有object都被释放,则释放该span到对应的可用列表,在释放的过程中,尝试将该span跟左右spans merge成更大的span

4. 如果当前thread cache的free 空间大于指定预置,归还部分空间给central list

5. central list也会试图通过释放可用span列表的最后几个span来将不用的空间归还给OS

tcmalloc向OS申请/释放资源是以span为单位的。

注意:tcmalloc向系统申请空间有三种方式:

sbrk,mmap,/dev/mem文件

,默认是三种都可以的,一种不行换另外一种。

注:大对象直接使用页级分配器(一个页是一个4K的对齐内存区域)从中央堆直接分配。



版权声明:本文为m0_56709806原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。