前言
最近筆者因?yàn)閒link集群運(yùn)行在kubernetes上,由于不可抗力導(dǎo)致pod重生说搅,job需要restart僵缺,在沒有開啟checkpoint的情況下获三,作業(yè)只要重啟就會頻繁被os kill,這明顯是堆外內(nèi)存超用的現(xiàn)象梳凛。
heap memory和direct memory被jvm控制了耿币,顯然不會被os kill,而是OOM韧拒,可以被flink 捕捉而爆出異常的淹接,被os kill只有托管給rocksdb的native memory了。
如何分析native memory的leak呢叛溢,就需要引入jemalloc蹈集。
什么是jemalloc
系統(tǒng)的物理內(nèi)存是有限的,而對內(nèi)存的需求是變化的, 程序的動態(tài)性越強(qiáng)雇初,內(nèi)存管理就越重要拢肆,選擇合適的內(nèi)存管理算法會帶來明顯的性能提升。
比如nginx靖诗, 它在每個連接accept后會malloc一塊內(nèi)存郭怪,作為整個連接生命周期內(nèi)的內(nèi)存池。 當(dāng)HTTP請求到達(dá)的時候刊橘,又會malloc一塊當(dāng)前請求階段的內(nèi)存池, 因此對malloc的分配速度有一定的依賴關(guān)系鄙才。
內(nèi)存管理可以分為三個層次,自底向上分別是:
- 操作系統(tǒng)內(nèi)核的內(nèi)存管理
- glibc層使用系統(tǒng)調(diào)用維護(hù)的內(nèi)存管理算法
- 應(yīng)用程序從glibc動態(tài)分配內(nèi)存后促绵,根據(jù)應(yīng)用程序本身的程序特性進(jìn)行優(yōu)化攒庵, 比如使用引用計(jì)數(shù)std::shared_ptr嘴纺,apache的內(nèi)存池方式等等。
當(dāng)然應(yīng)用程序也可以直接使用系統(tǒng)調(diào)用從內(nèi)核分配內(nèi)存浓冒,自己根據(jù)程序特性來維護(hù)內(nèi)存栽渴,但是會大大增加開發(fā)成本
glibc malloc的實(shí)現(xiàn)是ptmalloc2,其替代品tcmalloc 和 jemalloc稳懒。
tcmalloc
tcmalloc是Google開源的一個內(nèi)存管理庫闲擦, 作為glibc malloc的替代品。目前已經(jīng)在chrome场梆、safari等知名軟件中運(yùn)用墅冷。
根據(jù)官方測試報(bào)告,ptmalloc在一臺2.8GHz的P4機(jī)器上(對于小對象)執(zhí)行一次malloc及free大約需要300納秒或油。而TCMalloc的版本同樣的操作大約只需要50納秒寞忿。
jemalloc
jemalloc是facebook推出的, 最早的時候是freebsd的libc malloc實(shí)現(xiàn)顶岸。 目前在firefox罐脊、facebook服務(wù)器各種組件中大量使用。
對應(yīng)的git地址如下
https://github.com/jemalloc/jemalloc/blob/dev/INSTALL.md
jemalloc有一項(xiàng)功能蜕琴,對應(yīng)長時間運(yùn)行的程序可以trace內(nèi)存,見文檔[6]萍桌。
如果想更加詳細(xì)的了解這三者的性能和對比,可以參考文檔[5]
環(huán)境
筆者的flink集群版本是1.10.1凌简,運(yùn)行在1.17的kubernetes上上炎。
編譯jemalloc
下載
wget https://github.com/jemalloc/jemalloc/releases/download/5.2.1/jemalloc-5.2.1.tar.bz2
解壓
tar -zxvf jemalloc-5.2.1.tar.bz2
開始編譯
./configure --enable-prof --enable-stats --enable-debug --enable-fill
一定要加上--enable-prof 才可以使用heap-prof的功能
make
make install
對我們來說,需要的是
bin
lib
將文件打入flink鏡像中
bin
lib
打入flink鏡像
ADD jemalloc /opt/jemalloc/
不知道怎么自己構(gòu)建鏡像的可以參考
配置jemalloc
如果flink的運(yùn)行方式的native kubernetes雏搂,可以在構(gòu)建集群的腳本添加
-Dcontainerized.taskmanager.env.LD_PRELOAD: /opt/jemalloc/lib/libjemalloc.so.2
-Dcontainerized.taskmanager.env.MALLOC_CONF: prof:true,lg_prof_interval:25,lg_prof_sample:17,prof_prefix:/opt/state/jeprof.out
如果是on kubernetes的standalone藕施,那么需要修改deployment
env:
- name: LD_PRELOAD
value: /opt/jemalloc/lib/libjemalloc.so.2
- name: MALLOC_CONF
value: prof:true,lg_prof_interval:30,lg_prof_sample:17,prof_prefix:/opt/state/tmp/jeprof.out
配置解釋:
LD_PRELOAD: 將內(nèi)存分配從ptmalloc2改為libjemalloc.so.2
MALLOC_CONF: jemalloc的配置,prof_prefix是將生成的內(nèi)存文件dump到指定文件凸郑。lg_prof_interval:30 是 2^30 byte(1G)生成一個文件裳食,具體參數(shù)可以參考
https://github.com/jemalloc/jemalloc/blob/dev/INSTALL.md
進(jìn)入容器補(bǔ)充工具
可以到/opt/state/tmp/下看到很多jeprof.out開頭的heap文件
由于flink的容器是最簡化模式,會缺少很多工具芙沥,想要直接使用jeprof是會缺少很多的诲祸,需要補(bǔ)充下載
先將源改為國內(nèi)的源
在容器內(nèi)運(yùn)行
mv /etc/apt/sources.list /etc/apt/sources.list.bak
echo "deb http://mirrors.163.com/debian/ jessie main non-free contrib" >> /etc/apt/sources.list
echo "deb http://mirrors.163.com/debian/ jessie-proposed-updates main non-free contrib" >>/etc/apt/sources.list
echo "deb-src http://mirrors.163.com/debian/ jessie main non-free contrib" >>/etc/apt/sources.list
echo "deb-src http://mirrors.163.com/debian/ jessie-proposed-updates main non-free contrib" >>/etc/apt/sources.list
apt-get update
安裝對應(yīng)的工具
apt-get install -y binutils graphviz ghostscript
分析內(nèi)存
/opt/jemalloc/bin/jeprof --show_bytes `which java` /opt/state/tmp/jeprof.out.301.808.i808.heap
root@flink-taskmanager-69df85b5b9-dq42m:/opt/state/tmp# /opt/jemalloc/bin/jeprof --show_bytes `which java` /opt/state/tmp/jeprof.out.301.808.i808.heap
Using local file /usr/local/openjdk-8/bin/java.
Argument "MSWin32" isn't numeric in numeric eq (==) at /opt/jemalloc/bin/jeprof line 5124.
Argument "linux" isn't numeric in numeric eq (==) at /opt/jemalloc/bin/jeprof line 5124.
Using local file /opt/state/tmp/jeprof.out.301.808.i808.heap.
Welcome to jeprof! For help, type 'help'.
(jeprof) top
Total: 5580982945 B
2350833429 42.1% 42.1% 2350833429 42.1% os::malloc@8b2970
2002406207 35.9% 78.0% 2002406207 35.9% rocksdb::UncompressBlockContentsForCompressionType
1182793728 21.2% 99.2% 1183056000 21.2% rocksdb::Arena::AllocateNewBlock
11014112 0.2% 99.4% 13300172 0.2% rocksdb::LRUCacheShard::Insert
9440064 0.2% 99.6% 2011846271 36.0% rocksdb::BlockBasedTable::PartitionedIndexIteratorState::NewSecondaryIterator
6151347 0.1% 99.7% 6151347 0.1% std::string::_Rep::_S_create
3335701 0.1% 99.7% 3335701 0.1% readCEN
2621559 0.0% 99.8% 2621559 0.0% rocksdb::WritableFileWriter::Append
2381515 0.0% 99.8% 3581933 0.1% rocksdb::VersionSet::ProcessManifestWrites
2286059 0.0% 99.9% 2286059 0.0% rocksdb::LRUHandleTable::Resize
(jeprof)
可以導(dǎo)出成pdf或者svg
Output type:
--text Generate text report
--callgrind Generate callgrind format to stdout
--gv Generate Postscript and display
--evince Generate PDF and display
--web Generate SVG and display
--list=<regexp> Generate source listing of matching routines
--disasm=<regexp> Generate disassembly of matching routines
--symbols Print demangled symbol names found at given addresses
--dot Generate DOT file to stdout
--ps Generate Postcript to stdout
--pdf Generate PDF to stdout
--svg Generate SVG to stdout
--gif Generate GIF to stdout
--raw Generate symbolized jeprof data (useful with remote fetch)
/opt/jemalloc/bin/jeprof --show_bytes -svg `which java` /opt/state/tmp/jeprof.out.301.1009.i1009.heap > 105.svg
Refernce
[3]Using jemalloc to get to the bottom of a memory leak
[4]Debugging Java Native Memory Leaks