jemalloc是一个内存分配器,最开始出现在FreeBSD上的libc中,FreeBSD最早使用phkmalloc,但它不是在多处理器系统且支持多线程的情况下设计的,为了解决多处理器系统和多线程应用程序的可扩展性瓶颈,由此诞生jemalloc,它是Jason Evans 2005年进行开发的,因此叫"je"。在2007年的时候为了改善Firefox在3.0版中的内存使用情况,使用jemalloc解决了在Windows平台上的碎片问题,同时也给jemalloc带来了许多通用的增强功能。2009年Jason Evans改进jemalloc以处理Facebook服务器通常承受的极端负载,并增加了许多支持开发和监视的功能。2017年便由一个Facebook团队进行持续的开发和维护。
jemalloc在多处理器和多线程系统中具有很强的可扩展的并发支持,部分是通过使用多个arenas 来实现(从中进行分配原始内存块),并且以循环方式将线程分配给arenas。这意味着可以减少锁竞争,因为尽管多个线程可以并发调用malloc或free,但只有在它们共享相同的arenas时才会竞争。jemalloc强调避免碎片,它会尝试连续分配内存并且最大程度地减少内存使用总量。
由于CPU缓存,RAM和虚拟内存分页的影响,内存布局可能会对应用程序其余部分的运行速度产生重大影响。内存分配和释放并不是一个简单的问题。关于jemalloc算法的工作方式和更深入的分析请参见论文:“A Scalable Concurrent malloc(3) Implementation for FreeBSD”。
从jemalloc的github地址:https://github.com/jemalloc/jemalloc 下载压缩包,解压后进入目录执行安装,我的安装路径是/root/Download/jemalloc
,操作如下:
unzip jemalloc-dev.zip
mv jemalloc-dev jemalloc
cd jemalloc
./autogen.sh
make -j 6
make install
安装完成之后目录下多出一些文件,和.so文件:
对于以下ex_stats_print.c示例代码:
#include
#include
void do_something(size_t i) {
// Leak some memory.
malloc(i * 100);
}
int main(int argc, char **argv) {
for (size_t i = 0; i < 1000; i++) {
do_something(i);
}
// Dump allocator statistics to stderr.
malloc_stats_print(NULL, NULL, NULL);
return 0;
}
使用以下命令编译:
gcc ex_stats_print.c -o ex_stats_print.out -L/root/Download/jemalloc/lib -ljemalloc
编译成功之后执行文件./ex_stats_print.out
报找不到.so.2文件的错误:./ex_stats_print.out: error while loading shared libraries: libjemalloc.so.2: cannot open shared object file: No such file or directory
。这个错误解决在我之前的博客关于benchmark的Instruction Cache Misses测试中“常见错误及解决办法”里面有说到。
在此可采用以下命令解决:
echo /root/Download/jemalloc/lib >> /etc/ld.so.conf
ldconfig
再次执行./ex_stats_print.out
将得到以下结果:
以下用例内容来自jemalloc wiki:https://github.com/jemalloc/jemalloc/wiki/Use-Case
valgrind
的集成。如果jemalloc是用--enable-debug
指定配置的,则将编译各种断言以检测双精度释放,未对齐的指针等。这些检查对于正确运行应用程序不是必需的,但在开发过程中可能会有所帮助。禁用线程缓存(MALLOC_CONF=tcache:false
)往往会使jemalloc的内部断言在捕获应用程序错误(尤其是两次释放)时更加有效。--enable-fill
指定的配置的,则可以使用MALLOC_CONF=junk:true
在环境中进行设置,以告知malloc()
用0xa5字节填充对象,并用free()
释放内存。基本分配器统计
默认情况下,jemalloc使用线程本地存储进行对象缓存。对于前面使用的ex_stats_print.c示例代码,运行MALLOC_CONF=stats_print:true ./ex_stats_print.out
就能知道jemalloc在内部实际响应应用程序的内存分配活动在做什么,以改进分配器。
使用mallctl*()自我检查
对于以下ex_mallctl.c示例代码:
#include
#include
#include
#include
void do_something(size_t i)
{
// Leak some memory.
malloc(i * 100);
}
int main(int argc, char **argv)
{
size_t i, sz;
for (i = 0; i < 100; i++) {
do_something(i);
// Update the statistics cached by mallctl.
uint64_t epoch = 1;
sz = sizeof(epoch);
mallctl("epoch", &epoch, &sz, &epoch, sz);
// Get basic allocation statistics. Take care to check for
// errors, since --enable-stats must have been specified at
// build time for these statistics to be available.
size_t sz, allocated, active, metadata, resident, mapped;
sz = sizeof(size_t);
if (mallctl("stats.allocated", &allocated, &sz, NULL, 0) == 0
&& mallctl("stats.active", &active, &sz, NULL, 0) == 0
&& mallctl("stats.metadata", &metadata, &sz, NULL, 0) == 0
&& mallctl("stats.resident", &resident, &sz, NULL, 0) == 0
&& mallctl("stats.mapped", &mapped, &sz, NULL, 0) == 0) {
fprintf(stderr,
"Current allocated/active/metadata/resident/mapped: %zu/%zu/%zu/%zu/%zu\n",
allocated, active, metadata, resident, mapped);
}
}
return (0);
}
执行以下命令进行编译运行得到结果如下:
gcc ex_mallctl.c -o ex_mallctl.out -L/root/Download/jemalloc/lib -ljemalloc
Current allocated/active/metadata/resident/mapped: 111400/122880/2854856/2953216/6414336
Current allocated/active/metadata/resident/mapped: 122600/151552/2854984/2981888/6443008
Current allocated/active/metadata/resident/mapped: 145000/180224/2855112/3014656/6471680
Current allocated/active/metadata/resident/mapped: 165480/200704/2855240/3035136/6492160
Current allocated/active/metadata/resident/mapped: 194152/229376/2855368/3063808/6520832
Current allocated/active/metadata/resident/mapped: 199272/237568/2855624/3072000/6529024
Current allocated/active/metadata/resident/mapped: 219752/258048/2855752/3092480/6549504
Current allocated/active/metadata/resident/mapped: 232040/270336/2855880/3104768/6561792
....
MALLOC_CONF=prof_leak:true,lg_prof_sample:0,prof_final:true LD_PRELOAD=/root/Download/jemalloc/lib/libjemalloc.so.2 w
得到类似以下输出:
: ......
: ......
: ......
15:31:15 up 3:05, 1 user, load average: 0.14, 0.41, 0.37
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
root tty7 :0 12:27 3:05m 7:04 7:04 /usr/lib/xorg/Xorg :0 -seat seat0 -auth /var/run/lightdm/root/:0 -nolisten tcp vt7 -novtswitch
要生成发生泄漏的调用图的PDF,请运行:
jeprof --show_bytes --pdf `which w` jeprof.19678.0.f.heap > w.pdf
export MALLOC_CONF="prof:true,prof_prefix:jeprof.out"
对于以下prof.c示例代码,编译运行之后便能看到结果:
#include
#include
int main()
{
const char *fileName = "heap_info.out";
mallctl("prof.dump", NULL, NULL, &fileName, sizeof(const char *));
return 0;
}
在numa架构的服务器中使用SPEC CPU测试benchmark时,在配置文件中都会使用到jemalloc。关于服务器NUMA nodes相关信息可使用以下命令查看:
numactl --hardware
head /proc/cpuinfo
在config文件中,以aocc2.0 speed cfg
文件为例
...
EXTRA_PORTABILITY = -DSPEC_LP64
EXTRA_LIBS = -lamdlibm -lm -L/home/zgl/cpu2017/jemalloc/lib/64 -ljemalloc #-fopenmp=libomp -lomp
MATHLIBOPT = -lamdlibm
...
EXTRA_LIBS = -lpthread -ldl -lmvec -lamdlibm -L/home/zgl/cpu2017/jemalloc/lib/64 -ljemalloc