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 

: Leak approximation summary: ~54755672 bytes, ~1001 objects, >= 2 contexts
: 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 
#include 
#include 

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 
#include 
#include 

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/

你可能感兴趣的:(jemalloc内存泄漏分析)