tensorflow中一個(gè)環(huán)境變量引發(fā)的內(nèi)存泄漏血案

起因

最近信息流推薦的業(yè)務(wù)方在使用tensorflow進(jìn)行分布式訓(xùn)練時(shí)延窜,反饋說程序有內(nèi)存泄露的情況吵冒。詳細(xì)了解之后,現(xiàn)場(chǎng)情況是這樣的:

  • 數(shù)據(jù)從hdfs讀取,checkpoint也保存到hdfs
  • 對(duì)于推薦模型算谈,查表多,計(jì)算少料滥,所以跑在了CPU上然眼。而由于單個(gè)進(jìn)程的CPU利用率上不去,所以每個(gè)機(jī)器上都起了多個(gè)tensorflow進(jìn)程(當(dāng)然葵腹,如何提高單進(jìn)程時(shí)的程序性能高每,是另一個(gè)話題了,這里先不談)践宴。
  • 隨著運(yùn)行時(shí)間的增加鲸匿,系統(tǒng)的空閑物理內(nèi)存在逐漸減少,最終會(huì)引發(fā)Linux的OOM Killer殺掉某個(gè)進(jìn)程阻肩。而由于PS占用的物理內(nèi)存最大带欢,所以基本上都是PS被殺掉。

初次分析

盡管ps被kill烤惊,但內(nèi)存消耗卻不一定是ps引起的乔煞。為了進(jìn)一步確定問題,我首先觀察了下各進(jìn)程virtual memory和res memory的使用情況:

while true; do
    # 打印virtual memory和res memory
    ps ux | grep 'job_name=ps\|job_name=worker' | grep -v grep | awk '{print $5,$6}' 
    sleep 30
done

通過對(duì)內(nèi)存使用的觀察柒室,我大致總結(jié)了一些現(xiàn)象:

  1. 無論是ps還是worker渡贾,virtual memory都要比res memory大出不少來。這應(yīng)該是比較正常的現(xiàn)象雄右。
  2. ps的virtual memory和res memory都還維持在一個(gè)比較穩(wěn)定的狀態(tài)空骚,不像是有內(nèi)存泄漏的樣子。
  3. worker的virtual memory有緩慢的上漲擂仍,而res memory則比較快的進(jìn)行增長(zhǎng)囤屹,但也還遠(yuǎn)遠(yuǎn)沒有達(dá)到virtual memory的大小。由于一臺(tái)物理機(jī)上起了好幾個(gè)worker進(jìn)程防楷,所以物理內(nèi)存會(huì)消耗的比較快牺丙。

通過這幾點(diǎn),我開始腦補(bǔ)問題的原因:

  • ps的virtual和res memory都比較平穩(wěn)复局,所以應(yīng)該不像是內(nèi)存泄漏的樣子
  • worker的virtual memory增長(zhǎng)遠(yuǎn)沒有res memory快冲簿,這種情況有點(diǎn)像申請(qǐng)了一個(gè)大內(nèi)存池,然后池里面的內(nèi)存在發(fā)生著泄露亿昏。

出于這個(gè)原因峦剔,我就沒急著上gperftools這種內(nèi)存檢測(cè)工具〗枪常考慮到tensorflow進(jìn)程里由于hdfs的使用而嵌入了一個(gè)jvm吝沫,所以我覺得這搞不好是java的問題:申請(qǐng)了一大坨堆內(nèi)存呻澜,然后開始慢慢的把它們都用滿。

驗(yàn)證

為了驗(yàn)證猜想惨险,我先把代碼改成了讀本地羹幸,非常幸運(yùn)的是:?jiǎn)栴}消失了。所以很自然的辫愉,我認(rèn)為應(yīng)該是jvm申請(qǐng)了太大的堆內(nèi)存栅受,總體造成了物理內(nèi)存的浪費(fèi)。

于是我設(shè)置了下hdfs c接口的jvm參數(shù)恭朗,將堆內(nèi)存限制為1G:

export LIBHDFS_OPTS=-Xmx1g -Xms256m -Xmn128m

運(yùn)行了一段時(shí)間后屏镊,java開始報(bào)OutOfMemory:

java_oom.png

再來

雖然問題沒解決,但java OOM的異常堆棧給提供了一個(gè)很有用的信息:程序在創(chuàng)建hadoop Filesystem對(duì)象時(shí)出錯(cuò)了痰腮。翻一下tensorflow的代碼而芥,從注釋中你就會(huì)發(fā)現(xiàn)這其實(shí)是不符合程序本意的:

tensorflow_hadoop_source.png

tensorflow希望只有一個(gè)FileSystem的對(duì)象,但它依賴于hdfs的cache層來保證這一點(diǎn)膀值。所以棍丐,程序在運(yùn)行一段時(shí)間后,還會(huì)去創(chuàng)建新的FileSystem對(duì)象虫腋,非常不合理骄酗。

無奈只好開始啃hadoop的代碼稀余。發(fā)現(xiàn)c接口可以通過設(shè)置一個(gè)開關(guān)參數(shù)來打印FileSystem的泄漏情況:

hadoop_source.png

在tensorflow調(diào)用hdfs的代碼中加上這個(gè)參數(shù)后悦冀,F(xiàn)ileSystem對(duì)象的創(chuàng)建過程得到了更加清楚的展示:

leak_detail.png

至此,已經(jīng)開始逐漸浮出水面了:

  • 由于某種原因睛琳,tensorflow在調(diào)用hdfs進(jìn)行數(shù)據(jù)讀寫時(shí)盒蟆,每次都會(huì)創(chuàng)建一個(gè)新的FileSystem對(duì)象。而這些對(duì)象很有可能是一直在cache中存放而沒有進(jìn)行刪除的师骗。

后來历等,hdfs的同事通過對(duì)java dump文件的一些分析,也證實(shí)了這個(gè)結(jié)論辟癌。有關(guān)jvm的內(nèi)存分析工具寒屯,不再詳細(xì)展開,大家可以用jmap為關(guān)鍵字進(jìn)行搜索黍少。

root cause

找到內(nèi)存泄漏的來源后寡夹,就開始從代碼層面進(jìn)行分析,大體流程如下:

  • 我們?cè)谑褂胻ensorflow的時(shí)候厂置,會(huì)通過KERB_TICKET_CACHE_PATH環(huán)境變量來指定hdfs kerberos的ticket cache(如果你對(duì)kerberos不熟悉菩掏,可以看我的這篇文章)。而一旦設(shè)置了該環(huán)境變量后昵济,訪問hadoop就會(huì)創(chuàng)建一個(gè)新的UserGroupInformation智绸,從而創(chuàng)建一個(gè)新的FileSystem野揪。
  • 通過和hdfs的同學(xué)溝通,可以在程序外部執(zhí)行kinit瞧栗,并且將ticket cache放到默認(rèn)位置后斯稳,不設(shè)置該環(huán)境變量也可以訪問帶安全的hdfs。
  • 我們之所以使用了該環(huán)境變量迹恐,可能是由于受老的tensorflow文檔的影響平挑。但就hdfs而言,使用自定義路徑ticket cache接口的行為系草,的確也略微有些不太清晰通熄。

所以,最后通過去除這個(gè)環(huán)境變量找都,這個(gè)問題得以解決唇辨。

寫在最后

這么簡(jiǎn)單的一個(gè)問題,花了我其實(shí)有將近一周的時(shí)間調(diào)試能耻,回想下還是有些啼笑皆非的赏枚。整個(gè)調(diào)試過程的感慨如下:

  • 找bug有時(shí)候也是個(gè)運(yùn)氣活。想從風(fēng)馬牛不相及的現(xiàn)象回溯到原因中去晓猛,能夠找到懷疑的方向是非常重要的饿幅。而為了提高自己在方向判斷上的敏銳度,還是得努力擴(kuò)充自己在系統(tǒng)層面的知識(shí)面才行戒职。
  • 對(duì)于一個(gè)復(fù)雜的系統(tǒng)而言栗恩,把接口行為定義清楚,把文檔寫清楚洪燥,真的相當(dāng)重要磕秤。很多看似在開發(fā)階段節(jié)省下來的少量時(shí)間,在維護(hù)階段很有可能都得加倍償還回去捧韵。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末市咆,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子再来,更是在濱河造成了極大的恐慌蒙兰,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,602評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件芒篷,死亡現(xiàn)場(chǎng)離奇詭異搜变,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)梭伐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門痹雅,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人糊识,你說我怎么就攤上這事绩社∷だ叮” “怎么了?”我有些...
    開封第一講書人閱讀 152,878評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵愉耙,是天一觀的道長(zhǎng)贮尉。 經(jīng)常有香客問我,道長(zhǎng)朴沿,這世上最難降的妖魔是什么猜谚? 我笑而不...
    開封第一講書人閱讀 55,306評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮赌渣,結(jié)果婚禮上魏铅,老公的妹妹穿的比我還像新娘。我一直安慰自己坚芜,他們只是感情好览芳,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,330評(píng)論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著鸿竖,像睡著了一般沧竟。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上缚忧,一...
    開封第一講書人閱讀 49,071評(píng)論 1 285
  • 那天悟泵,我揣著相機(jī)與錄音,去河邊找鬼闪水。 笑死糕非,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的敦第。 我是一名探鬼主播峰弹,決...
    沈念sama閱讀 38,382評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼芜果!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起融师,我...
    開封第一講書人閱讀 37,006評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤右钾,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后旱爆,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體舀射,經(jīng)...
    沈念sama閱讀 43,512評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,965評(píng)論 2 325
  • 正文 我和宋清朗相戀三年怀伦,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了脆烟。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,094評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡房待,死狀恐怖邢羔,靈堂內(nèi)的尸體忽然破棺而出驼抹,到底是詐尸還是另有隱情,我是刑警寧澤拜鹤,帶...
    沈念sama閱讀 33,732評(píng)論 4 323
  • 正文 年R本政府宣布框冀,位于F島的核電站,受9級(jí)特大地震影響敏簿,放射性物質(zhì)發(fā)生泄漏明也。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,283評(píng)論 3 307
  • 文/蒙蒙 一惯裕、第九天 我趴在偏房一處隱蔽的房頂上張望温数。 院中可真熱鬧,春花似錦蜻势、人聲如沸帆吻。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽猜煮。三九已至,卻和暖如春败许,著一層夾襖步出監(jiān)牢的瞬間王带,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工市殷, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留愕撰,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,536評(píng)論 2 354
  • 正文 我出身青樓醋寝,卻偏偏與公主長(zhǎng)得像搞挣,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子音羞,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,828評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容