概述
本文讲述如何在c/c++程序中使用je_malloc内存管理模块来提升c/c++程序的性能。
引言
在编写c/c++服务器代码时,服务器的性能不仅决定于多线程/进程池模型的使用,还有很大程度上在于如何使用和管理内存。
目前比较流行的c/c++代码来看,nginx,memcached,mysql等都有自己的内存管理模块,而redis使用的是第三方的内存管理模块je_malloc或tc_malloc,但虽然使用的是第三方的内存管理模块redis的性能依然是不错的。
重要的是,redis支持多种数据结构,若内存的申请和释放都需要自己管理,编写的复杂度将会可想而知。
所以,若我们能够自己编写内存管理模块当然更好,但使用第三方的内存管理模块也是一个不错的选择。
jemalloc简介
jemalloc是有名的内存管理模块,可以替换libc的malloc,从而使得程序的性能得到提升。对于jemalloc的介绍这里不再说明,在baidu上一搜一大把。下面正式进入正题,讲述该内存管理模块在Linux系统下的编译和使用。
下载和编译
在网站https://github.com/jemalloc/jemalloc/releases下载最新版的jemalloc源码包。
- 解压:
wget https://github.com/jemalloc/jemalloc/releases/download/5.2.0/jemalloc-5.2.0.tar.bz2
tar xjvf jemalloc-5.2.0.tar.bz2
cd jemalloc-5.2.0/
- 配置
./configure --with-jemalloc-prefix=je_
注意:这一步确定要把jemalloc的函数编译成哪种形式,比如下面的配置就会把分配内存的函数编译成je_malloc的形式,把calloc编译成je_calloc等等。这样就不会和系统的libc的分配函数malloc冲突,因为若不指定该选项默认编译的分配函数是malloc。
- 编译
make
编译完成后在lib目录下回生成以下的几个库文件:
$ ls ./lib
libjemalloc.a libjemalloc_pic.a libjemalloc.so libjemalloc.so.2
其中libjemalloc.a是静态库,
libjemalloc.so.2是动态库,这里我使用的是静态库。
使用jemalloc
- 在将要使用的工程目录下创建两个目录:
cd ../
mkdir testproject
mkdir ./testproject/lib
mkdir ./testproject/include
并把在jemalloc库源码目录include下的jemalloc.h,jemalloc_defs.h和libjemalloc.a分别复制到include和lib目录下,并创建一下测试函数。
cp jemalloc-5.2.0/include/jemalloc/jemalloc.h ./testproject/include
cp jemalloc-5.2.0/include/jemalloc/jemalloc_defs.h ./testproject/include
cp libjemalloc.a ./testproject/lib
cd ./testproject
- 创建diymalloc.h 文件,内容如下:
#ifndef _DIYMALLOC_H_
#define _DIYMALLOC_H_
#include <jemalloc.h>
//define to jemalloc
#define malloc(size) je_malloc(size)
#define calloc(count,size) je_calloc(count,size)
#define realloc(ptr,size) je_realloc(ptr,size)
#define free(ptr) je_free(ptr)
#endif
- 创建dtest.c文件,内容如下:
#include <stdio.h>
#include "diymalloc.h"
int main(void)
{
char *pcon;
pcon = malloc(10*sizeof(char));
if (!pcon)
fprintf(stderr, "malloc failed!\n");
if (pcon != NULL) {
free(pcon);
pcon = NULL;
}
fprintf(stderr, "main end!\n");
return 0;
}
- 创建Makefile文件,内容如下:
CC=gcc
CFLAGS=-Wall -g
INCLUDES=-I ./include/
ALLOC_DEP=./lib/libjemalloc.a
ALLOC_LINK=$(ALLOC_DEP) -lpthread -ldl
dtest: dtest.o
$(CC) $(INCLUDES) $(CFLAGS) -o dtest dtest.o $(ALLOC_LINK)
dtest.o: dtest.c $(ALLOC_DEP)
$(CC) -c $(INCLUDES) $(CFLAGS) dtest.c
clean:
rm -f dtest dtest.o
- 编译并运行
make
./dtest
若没有任何错误,说明程序运行成功。下面,可以通过gdb来跟踪jemalloc的函数调用,从而可以分析jemalloc的具体实现。
- 可以使用gdb进行跟踪,查看jemalloc的执行流程
gdb ./dtest
通过gdb就可以调试jemalloc的实现代码了,具体的代码,可以自己一步一步的执行来分析。
(gdb) b main
Breakpoint 1 at 0x40174e: file dtest.c, line 8.
(gdb) r
Starting program: /diskb/testjemalloc/dtest
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Breakpoint 1, main () at dtest.c:8
8 pcon = malloc(10*sizeof(char));
(gdb) s
je_malloc (size=10) at src/jemalloc.c:2253
2253 if (unlikely(!tsd || !tsd_fast(tsd) || (size > SC_LOOKUP_MAXCLASS))) {
(gdb) pwd
Working directory /diskb/testjemalloc.
(gdb) n
2259 if (unlikely(ticker_trytick(&tcache->gc_ticker))) {
(gdb) n
2263 szind_t ind = sz_size2index_lookup(size);
(gdb) n
2293 void* ret = cache_bin_alloc_easy(bin, &tcache_success);
(gdb) n
2263 szind_t ind = sz_size2index_lookup(size);
(gdb) n
2266 usize = sz_index2size(ind);
(gdb) n
2293 void* ret = cache_bin_alloc_easy(bin, &tcache_success);
(gdb) n