博主介绍:程序喵大人
- 35- 资深C/C++/Rust/Android/iOS客户端开发
- 10年大厂工作经验
- 嵌入式/人工智能/自动驾驶/音视频/游戏开发入门级选手
- 《C++20高级编程》《C++23高级编程》等多本书籍著译者
- 更多原创精品文章,首发gzh,见文末
- 👇👇记得订阅专栏,以防走丢👇👇
😉C++基础系列专栏
😃C语言基础系列专栏
🤣C++大佬养成攻略专栏
🤓C++训练营
👉🏻个人网站
在现代多线程应用程序中,高效的内存管理至关重要。Google开发的TCMalloc(Thread-Caching Malloc)正是一个为此设计的内存分配器,它通过减少锁竞争和优化内存使用,提供了卓越的性能和可扩展性。本文将详细介绍TCMalloc的工作原理、核心组件、使用方法以及调优技巧,帮助C++开发工程师更好地理解和利用这一工具。
一、TCMalloc简介
TCMalloc,全称Thread-Caching Malloc,是Google开发的一个高性能内存分配器,旨在替代系统的默认内存分配器(如malloc、free、new、delete等)。它通过线程缓存和中央自由列表等机制,实现了高效的内存分配和回收,特别适用于多线程、高并发场景。
TCMalloc的核心优势在于其高效性和可扩展性。对于大多数对象来说,分配和释放速度非常快,且没有锁竞争。它支持两种缓存模式:每个线程缓存和每个逻辑CPU缓存,从而在不同线程数量下都能保持高效的性能。此外,TCMalloc还提供了灵活的内存使用策略,允许释放的内存重用于不同大小的对象,或返回给操作系统。
二、TCMalloc的工作原理
TCMalloc可以分为前端(Front-end)、中端(Middle-end)和后端(Back-end)三个部分。
-
前端(Front-end)
前端是一个缓存,负责为应用程序提供快速的内存分配和释放。它有两种实现方式:每个线程缓存(per-thread cache)和每个逻辑CPU缓存(per-CPU cache)。
前端缓存中的内存以自由列表的形式存储,每个大小类别对应一个自由列表。当请求分配内存时,如果前端缓存中有适当大小的空闲对象,则直接从列表中取出并返回;如果列表为空,则从中端获取一批对象放入前端缓存。
-
每个线程缓存:在这种模式下,每个线程都有一个独立的缓存,用于存储小对象的分配和回收。这减少了多线程环境下的锁竞争,提高了分配和释放速度。然而,随着线程数量的增加,内存占用也会相应增加。
-
每个逻辑CPU缓存:为了解决每个线程缓存模式下内存占用高的问题,TCMalloc引入了每个逻辑CPU缓存模式。在这种模式下,系统中的每个逻辑CPU都有自己的缓存,从中分配内存。这减少了内存占用,同时保持了高效的性能。
-
-
中端(Middle-end)
中端负责为前端补充缓存,并将内存归还给后端。它由传输缓存(Transfer Cache)和中央自由列表(Central Free List)组成。
-
传输缓存:传输缓存用于在不同线程之间快速传递内存。当一个线程释放内存时,如果另一个线程正在请求相同大小的内存,则可以通过传输缓存快速满足请求。
-
中央自由列表:中央自由列表管理着内存中的跨度(Spans),即一个或多个内存页的集合。当前端缓存中的自由列表为空时,它会从中央自由列表获取一批对象放入前端缓存。
-
-
后端(Back-end)
后端负责从操作系统获取内存,并管理大对象的分配和回收。它创建多种不同尺寸的页(Pages),如4KiB、8KiB、32KiB和256KiB等,以适应不同大小的内存需求。当请求分配大对象时,后端直接从页堆中分配所需大小的页。
三、TCMalloc的使用方法
-
安装TCMalloc
在Ubuntu/Debian系统上,可以通过以下命令安装TCMalloc:
sudo apt update sudo apt install -y libgoogle-perftools-dev
在CentOS/Red Hat系统上,可以使用以下命令:
sudo yum install -y gperftools gperftools-devel
另外,还可以通过源码编译安装TCMalloc。下载源码后,执行以下命令进行编译和安装:
git clone https://github.com/gperftools/gperftools.git cd gperftools ./autogen.sh ./configure make sudo make install
-
使用TCMalloc
TCMalloc提供了一个libtcmalloc.so动态库,加载后即可替代系统默认的内存分配器。有两种方式可以使用TCMalloc:
-
通过LD_PRELOAD动态加载:在运行程序时,使用LD_PRELOAD环境变量加载tcmalloc:
LD_PRELOAD=/usr/lib/libtcmalloc.so ./your_program
-
在编译链接时显式指定:在程序中链接tcmalloc动态库或静态库:
gcc -o your_program your_program.c -ltcmalloc
-
-
调优TCMalloc
TCMalloc提供了多个环境变量来控制其行为,例如内存分配粒度、线程缓存等。常用环境变量包括:
例如,要限制线程缓存的总大小为32MB,可以执行以下命令:
export TCMALLOC_MAX_TOTAL_THREAD_CACHE_BYTES=33554432 ./your_program
-
TCMALLOC_MAX_TOTAL_THREAD_CACHE_BYTES
:限制线程缓存的总大小。 -
TCMALLOC_PAGE_SIZE
:设置页的大小。
-
-
调试和监控TCMalloc
TCMalloc提供了调试工具和统计信息,帮助开发者监控内存分配情况。例如,可以通过发送SIGUSR1信号给正在运行的程序,打印内存分配统计信息:
kill -SIGUSR1 <pid>
此外,还可以使用TCMalloc提供的API(如MallocExtension)在程序中获取内存使用统计数据。
四、TCMalloc的应用场景和限制
TCMalloc在多线程程序中表现出更好的可扩展性和一致性,尤其是在小对象分配上。然而,随着分配对象大小的增加,性能有所下降。在32KB对象大小附近,性能有一个明显的下降,因为这是线程缓存中对象的最大尺寸。此外,TCMalloc可能不适用于未链接到libpthread.so的系统,且不会将内存返回给系统。
码字不易,欢迎大家点赞,关注,评论,谢谢!
C++训练营
专为校招、社招3年工作经验的同学打造的1V1 C++训练营,量身定制学习计划、每日代码review,简历优化,面试辅导,已帮助多名学员获得大厂offer!