目录

​一. 安装​

​二. 使用​

​使用方法​

​对比测试​

​三、原理​

​四、问题或质疑​

​为什么测试的TCMalloc不靠谱,性能反而差了​

​tcmalloc是尬尴的存在?​

​五、其他相似的库Jemalloc​



一. 安装

  tcmalloc在gperftools之中,故想要使用tcmalloc,就得先安装gperftools。在linux下,其安装步骤如下:

  tar xzvf gperftools-2.1.tar.gz (或者用git clone https://github.com/gperftools/gperftools.git下载)

cd gperftools-2.1

./autogen.sh

./configure --enable-frame-pointers

make

make install

  这里需要注意一点,在linux下,如果直接./configure,那么make时会报出编译错误:error Cannot calculate stack trace: will need to write for your environment。解决方法如上所示,在configure时加入选项--enable-frame-pointers。

  如果想定制化安装,请自行参阅gperftools的安装文档,即源码包中的INSTALL文件。

二. 使用

使用方法

  对于tcmalloc的使用,还是用程序来说明吧。

  tcmalloc_sample.cpp:

#include <iostream>
#include <google/tcmalloc.h>

int main()
{
char *cp = (char *)tc_malloc(23 * sizeof(char));
tc_free(cp);
cp = NULL;
return 0;
}

  注:以下的步骤都是以第一部分所述的安装方式为前提而进行的。

  1. 编译:g++ -o tcmalloc_sample -g tcmalloc_sample.cpp -ltcmalloc

  2. 运行: ./tcmalloc_sample

  如果遇到以下错误:

  error while loading shared libraries: libtcmalloc.so.4: cannot open shared object file: No such file or directory

  那么就需要为系统指定libtcmalloc的加载路径:

  1. echo /usr/local/lib > /etc/ld.so.conf.d/libtcmalloc.conf

  2. ldconfig

  现在运行./tcmallco_sample命令,就不会出现上面提到的错误了。

对比测试

tmtest.cpp

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <sys/time.h>
#include <thread>

#define MAX_COUNT 1000*1000
using namespace std;

void fun(int i)
{
//char *ptr = (char *)malloc(i);
//free(ptr);
char *ptr = new char[1];
delete[] ptr;
}

void fun_thread(void )
{
int i = 0;
int j = 1;

while(i++ < MAX_COUNT)
{
j ++;
fun(j);

if ( j > 1024 )
j = 1;
}
}

#define MSECOND 1000000

int main()
{

struct timeval tpstart, tpend;
float timeuse;

gettimeofday(&tpstart, NULL);

thread t(fun_thread);
t.join();

int i = 0;
int j = 1;

while(i++ < MAX_COUNT)
{

j ++;
fun(i);
if ( j > 1024 )
j = 1;
//usleep(1);
}

gettimeofday(&tpend, NULL);

timeuse = MSECOND * (tpend.tv_sec - tpstart.tv_sec) + tpend.tv_usec - tpstart.tv_usec;
timeuse /= MSECOND;
printf("Used Time:%f\n", timeuse);
return 0;
}

#正常

[root@localhost test]# g++ tmtest.cpp -o 1 -lpthread

[root@localhost test]# ./tmtest

Used Time:5.336594

#替换malloc

[root@localhost test]# g++ tmtest.cpp -o 1 -lpthread -ltcmalloc

[root@localhost test]# ./tmtest

Used Time:0.208050

三、原理

(​​https://www.jianshu.com/p/7c55fbdef679​​)

(原理简单概括:内存池要解决的问题之一就是频繁的申请与释放,一般是在预分配里先找,有合适的用合适的,没有合适的再向系统申请,然后记录这块内存,最开始的版本大部分是统一管理,后面分了线程本地,线程本地解决了同步用锁,效率更高了)

tcmalloc为什么快:

使用了thread cache(线程cache),小块的内存分配都可以从cache中分配,这样再多线程分配内存的情况下,可以减少锁竞争

tcmalloc将内存请求分为两类,大对象请求小对象请求,大对象为>=32K的对象。

tcmalloc会为每个线程分配本地缓存,小对象请求可以直接从本地缓存获取,如果没有空闲内存,则从central heap中一次性获取一连串小对象。大对象是直接使用页级分配器(page-level allocator)从Central page Heap中进行分配,即一个大对象总是按页对齐的。

tcmalloc对于小内存,按8的整数次倍分配,对于大内存,按4K的整数次倍分配。

当某个线程缓存中所有对象的总大小超过2MB的时候,会进行垃圾收集。垃圾收集阈值会自动根据线程数量的增加而减少,这样就不会因为程序有大量线程而过度浪费内存。

【性能】tcmalloc 使用和原理tcmalloc为什么快:_mysql

tcmalloc为每个线程分配一个thread-local cache,小对象的分配直接从thread-local cache中分配。根据需要将对象从CentralHeap中移动到thread-local cache,同时定期的用垃圾回收器把内存从thread-local cache回收到Central free list中。

链接:https://www.jianshu.com/p/7c55fbdef679

10、总结

tcmalloc的内存分配分为四层:

ThreadCache(用于小对象分配):线程本地缓存,每个线程独立维护一个该对象,多线程在并发申请内存时不会产生锁竞争。

CentralCache(Central free list,用于小对象分配):全局cache,所有线程共享。当thread cache空闲链表为空时,会批量从CentralCache中申请内存;当thread cache总内存超过阈值,会进行内存垃圾回收,将空闲内存返还给CentralCache。

Page Heap(小/大对象):全局页堆,所有线程共享。对于小对象,当centralcache为空时,会从page heap中申请一个span;当一个span完全空闲时,会将该span返还给page heap。对于大对象,直接从page heap中分配,用完直接返还给page heap。

系统内存:当page cache内存用光后,会通过sbrk、mmap等系统调用向OS申请内存。

【性能】tcmalloc 使用和原理tcmalloc为什么快:_内存分配_02

【性能】tcmalloc 使用和原理tcmalloc为什么快:_内存分配_03

背景:

今天对服务器进行压测,模拟的请求量到4万次/分的时候,进程的CPU占用就已经达到400%了(也就是把四个核都占到100%)。其实模拟的请求数据都是单一的,不会因为BUG的原因导致CPU过高。怀疑是代码里的STL用得过多,加之ttserver和memcached大量的读操作——大量的小内存分配可能带来全局的锁竞争,从而可能使得CPU过高。

之前听说过google的tcmalloc是一个很好的线程内的内存分配器,能够提高内存分配的性能。正好今天试试能不能改善我的代码的性能。
安装的过程如下:
#1、到google下载代码:
cd /home/ahfu/temp
wget ​​https://gperftools.googlecode.com/files/gperftools-2.1.tar.gz​

#解压
tar -zxvf google-perftools-1.4.tar.gz
#看看说明
cd google-perftools-1.4
./configure -h
#选择简单的安装
mkdir -p /home/ahfu/temp/tcmalloc
./configure --disable-cpu-profiler --disable-heap-profiler --disable-heap-checker --disable-debugalloc --enable-minimal --disable-shared
make && make install
#在makefile里面加入一行就行
LIB = "/home/ahfu/temp/tcmalloc/lib/libtcmalloc_minimal.a"
#  g++ .....   ${LIB}

再进行压测!
大吃一惊!
请求量模拟到28万次/分后,CPU占用还不到20%!!!

四、问题或质疑

为什么测试的TCMalloc不靠谱,性能反而差了​

发现确实在release下性能用了tcmalloc后提升很多时间缩短了100%以上,但是debug下还是不如不用tcmal的

tcmalloc是尬尴的存在?​

“专用的‘对象池’可以比通用的‘内存池’性能高两个数量级。

通过宏定义(DECL_MEM_POOL, IMPL_MEM_POOL)可以很快速的为class增加pool能力,还可以在单线程的环境下去掉锁。

真正要性能的程序不会整天去分配内存。性能要求没那么高的程序glibc就够了。

所以,tcmalloc是一个很尴尬的东西。

ps:tcmalloc据说可以用来优化mysql(让mysql加载tcmalloc来代替glibc的相应函数)。不过mysql的性能瓶颈在于磁盘、索引、缓存,替换malloc能有多少作用很值得怀疑。”

五​​、​​其他相似的库Jemalloc

ptmalloc是glibc默认的malloc库。

Jemalloc和tcmalloc 区别:

《ptmalloc,tcmalloc和jemalloc内存分配策略研究》​​http://www.360doc.cn/mip/435803027.html​

总结

    Jemalloc设计上比前两个复杂地多,其内部使用了红黑树管理分页和内存块。并且对内存分配粒度分类地更细。这导致一方面比ptmalloc的锁争用要少,另一方面很多索引和查找都能回归到指数级别,方便了很多复杂功能的实现。而且在大内存分配上,内存碎片也会比tcmalloc少。但是也正是因为他的结构比较复杂,记录了很多meta,所以在分配很多小内存的时候记录meta数据的空间会略微多于tcmalloc。但是又不像ptmalloc那样每一个内存块都有一个header,而采用全局的bitmap记录状态,所以大量小内存的时候,会比ptmalloc消耗的额外内存小。

 

大总结

    看这些个分配器的分配机制,可见这些内存管理机制都是针对小内存分配和管理。对大块内存还是直接用了系统调用。所以在程序中应该尽量避免大内存的malloc/new、free/delete操作。另外这个分配器的最小粒度都是以8字节为单位的,所以频繁分配小内存,像int啊bool啊什么的,仍然会浪费空间。经过测试无论是对bool、int、short进行new的时候,实际消耗的内存在ptmalloc和tcmalloc下64位系统地址间距都是32个字节。大量new测试的时候,ptmalloc平均每次new消耗32字节,tcmalloc消耗8字节(我想说ptmalloc弱爆啦,而且tcmalloc)。所以大量使用这些数据的时候不妨用数组自己维护一个内存池,可以减少很多的内存浪费。(原来STL的map和set一个节点要消耗近80个字节有这么多浪费在这里了啊)

    而多线程下对于比较大的数据结构,为了减少分配时的锁争用,最好是自己维护内存池。单线程的话无所谓了,呵呵。不过自己维护内存池是增加代码复杂度,减少内存管理复杂度。但是我觉得,255个分页以下(1MB)的内存话,tcmalloc的分配和管理机制已经相当nice,没太大必要自己另写一个。

    另外,Windows下内存分配方式不知道,不同类型(int、short和bool)连续new的地址似乎是隔开的,可能是内部实现的粒度更小,不同size的class更多。测试10M次new的时候,debug模式下明显卡顿了一下,平均每次new的内存消耗是52字节(32位)和72字节(64位)[header更复杂?]。但是Release模式下很快,并且平均每次new的内存消耗是20字节(32位)和24字节(64位)。可以猜测VC的malloc的debug模式还含有挺大的debug信息。是不是可以得出他的header里,Release版本只有1个指针,Debug里有5个指针呢?

更多详细文档:​​https://www.jianshu.com/p/11082b443ddf​

【性能】tcmalloc 使用和原理tcmalloc为什么快:_内存分配_03