jemalloc内存泄漏分析

jemalloc 是一款内存分配器, 除了可以提高分配内存的效率之外。jemalloc还可以通过profiling机制来发现并定位内存泄漏。

1. 安装

官方提供的install教程:
https://github.com/jemalloc/jemalloc/blob/dev/INSTALL.md

git clone https://github.com/jemalloc/jemalloc .

./configure --enable-prof
make 
make install

make后在jemalloc/lib下面也可以看到这几个库:

➜  lib git:(dev) ls
libjemalloc.a  libjemalloc_pic.a  libjemalloc.so  libjemalloc.so.2

make install生成的文件
里面有两个重要文件:lib/libjemalloc.so.2; bin/jeprof

➜  jemalloc git:(dev) make install
/usr/bin/install -c -d /usr/local/bin
'bin/jemalloc-config' -> '/usr/local/bin/jemalloc-config'
'bin/jemalloc.sh' -> '/usr/local/bin/jemalloc.sh'
'bin/jeprof' -> '/usr/local/bin/jeprof'
/usr/bin/install -c -d /usr/local/include/jemalloc
'include/jemalloc/jemalloc.h' -> '/usr/local/include/jemalloc/jemalloc.h'
/usr/bin/install -c -d /usr/local/lib
/usr/bin/install -c -v -m 755 lib/libjemalloc.so.2 /usr/local/lib
'lib/libjemalloc.so.2' -> '/usr/local/lib/libjemalloc.so.2'
ln -sf libjemalloc.so.2 /usr/local/lib/libjemalloc.so
/usr/bin/install -c -d /usr/local/lib
'lib/libjemalloc.a' -> '/usr/local/lib/libjemalloc.a'
'lib/libjemalloc_pic.a' -> '/usr/local/lib/libjemalloc_pic.a'
/usr/bin/install -c -d /usr/local/lib/pkgconfig
'jemalloc.pc' -> '/usr/local/lib/pkgconfig/jemalloc.pc'
Missing xsltproc.  doc/jemalloc.html not (re)built.
/usr/bin/install -c -d /usr/local/share/doc/jemalloc
'doc/jemalloc.html' -> '/usr/local/share/doc/jemalloc/jemalloc.html'
Missing xsltproc.  doc/jemalloc.3 not (re)built.
/usr/bin/install -c -d /usr/local/share/man/man3
'doc/jemalloc.3' -> '/usr/local/share/man/man3/jemalloc.3'

2. jemalloc的使用

查看jemalloc的wiki,上面介绍了appliacation几种接入jemalloc的方法。第一种,可以使用LD_PRELOAD的方式,去指定jemalloc.so,根据链接的顺序这个库会替换掉原来的malloc(这种也是一个hook malloc的方案)。第二种,可以在编译的时候指定 g++ xxxx -ljemalloc。具体可以查看这个wiki: https://github.com/jemalloc/jemalloc/wiki/Getting-Started

// 随便写个测试程序
g++ test1.cpp -ljemalloc    
./a.out 
./a.out: error while loading shared libraries: libjemalloc.so.2: cannot open shared object file: No such file or directory

# 遇到这种问题,可以切到root
echo /usr/local/lib >> /etc/ld.so.conf
ldconfig

# 当然编译的时候 指定rpath也可以
g++ test.cpp -ljemalloc -Wl,-rpath="/usr/local/lib" 
  • 如何开启jemalloc的特性

Once you have jemalloc integrated into your application, you can use special features in a variety of ways:

  • Set the /etc/malloc.conf symlink or MALLOC_CONF environment variable to tune jemalloc, e.g.
export MALLOC_CONF="prof:true,lg_prof_sample:1,prof_accum:false,prof_prefix:jeprof.out"
  • Directly invoke jemalloc features in the application:
    Compile the following code as such:
cc ex_stats_print.c -o ex_stats_print -I`jemalloc-config --includedir` \
-L`jemalloc-config --libdir` -Wl,-rpath,`jemalloc-config --libdir` \
-ljemalloc `jemalloc-config --libs`

$ g++ test1.cpp
$ MALLOC_CONF=prof_leak:true,lg_prof_sample:0,prof_final:true LD_PRELOAD=/usr/local/lib/libjemalloc.so.2 ./a.out 

<jemalloc>: Leak approximation summary: ~54755672 bytes, ~1001 objects, >= 2 contexts
<jemalloc>: Run jeprof on dump output for leak detail

jeprof 查看内存细节

jeprof
/usr/local/bin/jeprof a.out jeprof.2545447.0.f.heap 
Using local file a.out.
Using local file jeprof.2545447.0.f.heap.
Welcome to jeprof!  For help, type 'help'.
(jeprof) top
Total: 52.2 MB
    52.2 100.0% 100.0%     52.2 100.0% prof_backtrace_impl
     0.0   0.0% 100.0%     52.1  99.9% __libc_start_main
     0.0   0.0% 100.0%      0.1   0.1% _dl_init
     0.0   0.0% 100.0%      0.1   0.1% _dl_start_user
     0.0   0.0% 100.0%     52.1  99.9% _start
     0.0   0.0% 100.0%      0.1   0.1% call_init.part.0
     0.0   0.0% 100.0%     52.1  99.9% do_something
     0.0   0.0% 100.0%     52.2 100.0% je_prof_backtrace
     0.0   0.0% 100.0%     52.2 100.0% je_prof_tctx_create
     0.0   0.0% 100.0%     52.1  99.9% main
jeprof  --show_bytes --pdf a.out jeprof.2545447.0.f.heap > show.pdf

  • 每1MB dump一次

其中lg_prof_interval:20中的20表示1MB(2^20),prof:true是打开profiling。运行程序时,每分配(大约)1MB就会dump产生一个文件。

$ export MALLOC_CONF="prof:true,lg_prof_interval:20"
$ LD_PRELOAD=/usr/local/jemalloc-5.1.0/lib/libjemalloc.so.2  ./a.out
$ ll

jeprof工具不仅可以查看详细信息或者生成调用路径图(如上所示),还可以用来比较两个dump(显示增量部分):

# /usr/local/jemalloc-5.1.0/bin/jeprof a.out --base=jeprof.34584.2.i2.heap jeprof.34584.3.i3.heap
Using local file a.out.
Using local file jeprof.34584.3.i3.heap.
Welcome to jeprof!  For help, type 'help'.
(jeprof) top

其中--base指定比较的基础。如上例,dump jeprof.34584.3.i3.heap的时候,分配了5.8 MB内存,do_something和do_something_else分别占81.8%和18.2%;但和dump jeprof.34584.2.i2.heap的时候相比,多分配了1.6MB内存,do_something和do_something_else分别占66.2%和33.8%。


  • 内存使用达到新高时,dump一次内存
$ export MALLOC_CONF="prof:true,prof_gdump:true"
$ LD_PRELOAD=/usr/local/jemalloc-5.1.0/lib/libjemalloc.so.2  ./a.out
  • 手动在代码中调用dump,记录内存现场
#include <stdio.h>
#include <stdlib.h>
#include <jemalloc/jemalloc.h>

void do_something(size_t i)
{
  // Leak some memory.
  malloc(i * 1024);
}

void do_something_else(size_t i)
{
  // Leak some memory.
  malloc(i * 4096);
}

int main(int argc, char **argv)
{
  size_t i, sz;

  for (i = 0; i < 80; i++)
  {
    do_something(i);
  }

  mallctl("prof.dump", NULL, NULL, NULL, 0);

  for (i = 0; i < 40; i++)
  {
    do_something_else(i);
  }

  mallctl("prof.dump", NULL, NULL, NULL, 0);

  return (0);
}

// gcc -I/usr/local/jemalloc-5.1.0/include test.c -L/usr/local/jemalloc-5.1.0/lib -ljemalloc

  • 程序运行稳定之后,dump一次内存
    程序启动的时候,势必要分配内存,我们查找内存泄漏的时候,往往更关注程序在稳定状态时的内存分配:只要程序启动完成之后内存不再增长,就没有严重的泄漏问题。所以,稳定状态的内存profiling往往更有意义。设置MALLOC_CONF=prof_active:false,使得程序在启动的时候profiling是disabled;程序启动完成后,再通过mallctl(“prof.active”)来enable profiling;或者定时enable。
#include <stdio.h>
#include <stdlib.h>
#include <jemalloc/jemalloc.h>

void do_something(size_t i)
{
  // Leak some memory.
  malloc(i * 1024);
}

void do_something_else(size_t i)
{
  // Leak some memory.
  malloc(i * 4096);
}

int main(int argc, char **argv)
{
  size_t i, sz;

  //initialization ...

  for (i = 0; i < 80; i++)
  {
    do_something(i);
  }

  //enter into steady-state...

  bool active = true;
  mallctl("prof.active", NULL, NULL, &active, sizeof(bool));

  for (i = 0; i < 40; i++)
  {
    do_something_else(i);
  }

  mallctl("prof.dump", NULL, NULL, NULL, 0);

  return (0);
}
➜  jemalloc_test export MALLOC_CONF="prof:true,prof_active:false,prof_prefix:jeprof.out"
➜  jemalloc_test LD_PRELOAD=/usr/local/lib/libjemalloc.so.2 ./a.out

用jeprof查看,发现只有steady-state之后的内存分配:

➜  jemalloc_test jeprof a.out jeprof.out.2789991.0.m0.heap 
Using local file a.out.
Using local file jeprof.out.2789991.0.m0.heap.
Welcome to jeprof!  For help, type 'help'.
(jeprof) top
Total: 3.2 MB
     3.2 100.0% 100.0%      3.2 100.0% prof_backtrace_impl
     0.0   0.0% 100.0%      3.2 100.0% __libc_start_main
     0.0   0.0% 100.0%      3.2 100.0% _start
     0.0   0.0% 100.0%      3.2 100.0% do_something_else
     0.0   0.0% 100.0%      3.2 100.0% je_prof_backtrace
     0.0   0.0% 100.0%      3.2 100.0% je_prof_tctx_create
     0.0   0.0% 100.0%      3.2 100.0% main
     0.0   0.0% 100.0%      3.2 100.0% prof_alloc_prep

  • 定时profiling

通过jeprof a.out --base=prof.1 prof.2来比较这两个dump,这可以突显出稳定状态下程序的内存分配行为。

bool active;

mallctl("prof.dump", NULL, NULL, NULL, 0);    //生成prof.1

active = true;
mallctl("prof.active", NULL, NULL, &active, sizeof(bool));

//sleep 30 seconds

active = false;
mallctl("prof.active", NULL, NULL, &active, sizeof(bool));

//sleep 30 seconds

mallctl("prof.dump", NULL, NULL, NULL, 0);   //生成prof.2

  • 实践
  1. 为了方便直接在编译的时候,指定jemalloc.so,这样可以在程序运行前LD_PRELOAD指定jemalloc。
  2. 在程序运行的时候,执行MALLOC_CONF环境变量。 这样程序就直接可以运行了,不需要在程序执行前在设置各种环境变量了。
// 在服务器程序启动稳定后,设置环境变量,并且开启内存profile
setenv("MALLOC_CONF", "prof:true,prof_active:false,prof_prefix:jeprof.out", 1);
bool active = true;
mallctl("prof.active", NULL, NULL, &active, sizeof(bool));
  1. 周期性的dump内存
mallctl("prof.dump", NULL, NULL, NULL, 0);
  1. 使用jeprof命令对比内存
方式1:(不太推荐,只能看到部分调用链)
jeprof a.out jeprof.out.28954.1.m1.heap 
> top

方式2:
jeprof --show_bytes --pdf  a.out jeprof.out.28954.1.m1.heap > je.pdf
依赖:
yum -y install ghostscript
yum install graphviz


方式3: (可以在服务器启动后 dump一份内存作为基准,然后两份dump文件进行对比)
jeprof --dot a.out -base=jeprof.out.28954.0.m0.heap jeprof.out.28954.1.m1.heap 

执行后会生成图片的信息,粘贴后在这里查看:
http://www.webgraphviz.com/

参考:

https://github.com/jemalloc/jemalloc
https://github.com/jemalloc/jemalloc/wiki/Use-Case%3A-Heap-Profiling

https://zhuanlan.zhihu.com/p/462516856
https://www.yuanguohuo.com/2019/01/02/jemalloc-heap-profiling/

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,869评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,716评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,223评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,047评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,089评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,839评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,516评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,410评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,920评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,052评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,179评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,868评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,522评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,070评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,186评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,487评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,162评论 2 356

推荐阅读更多精彩内容