jemalloc 是一款內(nèi)存分配器, 除了可以提高分配內(nèi)存的效率之外。jemalloc還可以通過profiling機(jī)制來發(fā)現(xiàn)并定位內(nèi)存泄漏。
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下面也可以看到這幾個(gè)庫:
? lib git:(dev) ls
libjemalloc.a libjemalloc_pic.a libjemalloc.so libjemalloc.so.2
make install生成的文件
里面有兩個(gè)重要文件: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,根據(jù)鏈接的順序這個(gè)庫會替換掉原來的malloc(這種也是一個(gè)hook malloc的方案)。第二種,可以在編譯的時(shí)候指定 g++ xxxx -ljemalloc。具體可以查看這個(gè)wiki: https://github.com/jemalloc/jemalloc/wiki/Getting-Started
// 隨便寫個(gè)測試程序
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
# 當(dāng)然編譯的時(shí)候 指定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.confsymlink orMALLOC_CONFenvironment 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 查看內(nèi)存細(xì)節(jié)
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一次
其中l(wèi)g_prof_interval:20中的20表示1MB(2^20),prof:true是打開profiling。運(yùn)行程序時(shí),每分配(大約)1MB就會dump產(chǎn)生一個(gè)文件。
$ 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工具不僅可以查看詳細(xì)信息或者生成調(diào)用路徑圖(如上所示),還可以用來比較兩個(gè)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指定比較的基礎(chǔ)。如上例,dump jeprof.34584.3.i3.heap的時(shí)候,分配了5.8 MB內(nèi)存,do_something和do_something_else分別占81.8%和18.2%;但和dump jeprof.34584.2.i2.heap的時(shí)候相比,多分配了1.6MB內(nèi)存,do_something和do_something_else分別占66.2%和33.8%。
- 內(nèi)存使用達(dá)到新高時(shí),dump一次內(nèi)存
$ export MALLOC_CONF="prof:true,prof_gdump:true"
$ LD_PRELOAD=/usr/local/jemalloc-5.1.0/lib/libjemalloc.so.2 ./a.out
- 手動在代碼中調(diào)用dump,記錄內(nèi)存現(xiàn)場
#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
- 程序運(yùn)行穩(wěn)定之后,dump一次內(nèi)存
程序啟動的時(shí)候,勢必要分配內(nèi)存,我們查找內(nèi)存泄漏的時(shí)候,往往更關(guān)注程序在穩(wěn)定狀態(tài)時(shí)的內(nèi)存分配:只要程序啟動完成之后內(nèi)存不再增長,就沒有嚴(yán)重的泄漏問題。所以,穩(wěn)定狀態(tài)的內(nèi)存profiling往往更有意義。設(shè)置MALLOC_CONF=prof_active:false,使得程序在啟動的時(shí)候profiling是disabled;程序啟動完成后,再通過mallctl(“prof.active”)來enable profiling;或者定時(shí)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查看,發(fā)現(xiàn)只有steady-state之后的內(nèi)存分配:
? 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
- 定時(shí)profiling
通過jeprof a.out --base=prof.1 prof.2來比較這兩個(gè)dump,這可以突顯出穩(wěn)定狀態(tài)下程序的內(nèi)存分配行為。
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
- 實(shí)踐
- 為了方便直接在編譯的時(shí)候,指定jemalloc.so,這樣可以在程序運(yùn)行前LD_PRELOAD指定jemalloc。
- 在程序運(yùn)行的時(shí)候,執(zhí)行MALLOC_CONF環(huán)境變量。 這樣程序就直接可以運(yùn)行了,不需要在程序執(zhí)行前在設(shè)置各種環(huán)境變量了。
// 在服務(wù)器程序啟動穩(wěn)定后,設(shè)置環(huán)境變量,并且開啟內(nèi)存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));
- 周期性的dump內(nèi)存
mallctl("prof.dump", NULL, NULL, NULL, 0);
- 使用jeprof命令對比內(nèi)存
方式1:(不太推薦,只能看到部分調(diào)用鏈)
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: (可以在服務(wù)器啟動后 dump一份內(nèi)存作為基準(zhǔn),然后兩份dump文件進(jìn)行對比)
jeprof --dot a.out -base=jeprof.out.28954.0.m0.heap jeprof.out.28954.1.m1.heap
執(zhí)行后會生成圖片的信息,粘貼后在這里查看:
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/