日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 综合教程 >内容正文

综合教程

如何定位 Node.js 的内存泄漏

發(fā)布時(shí)間:2023/12/13 综合教程 49 生活家
生活随笔 收集整理的這篇文章主要介紹了 如何定位 Node.js 的内存泄漏 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

基礎(chǔ)知識(shí)

Node.js 進(jìn)程的內(nèi)存管理,都是有 V8 自動(dòng)處理的,包括內(nèi)存分配和釋放。那么 V8 什么時(shí)候會(huì)將內(nèi)存釋放呢?

在 V8 內(nèi)部,會(huì)為程序中的所有變量構(gòu)建一個(gè)圖,來(lái)表示變量間的關(guān)聯(lián)關(guān)系,當(dāng)變量從根節(jié)點(diǎn)無(wú)法觸達(dá)時(shí),就意味著這個(gè)變量不會(huì)再被使用了,就是可以回收的了。
而這個(gè)回收是一個(gè)過(guò)程性的,從快速 GC 到 最后的 Full GC,是需要一段時(shí)間的。
另外,F(xiàn)ull GC 是有觸發(fā)閾值的,所以可能會(huì)出現(xiàn)內(nèi)存長(zhǎng)期占用在一個(gè)高值,也可以算是一種內(nèi)存泄漏,可以從《一次 Node.js 應(yīng)用內(nèi)存暴漲分析》中找到例子。還有一種就是引用不釋放,導(dǎo)致無(wú)法進(jìn)入 GC 環(huán)節(jié),并且一直產(chǎn)生新的占用,這一般會(huì)發(fā)生在 Javascript 層面。

所以,定位內(nèi)存泄漏問(wèn)題,一般方案就是找那些不被使用又不會(huì)被釋放的變量,處理了這些變量,問(wèn)題一般就可以解決了。如果是 Node.js 底層變量不釋放,除了提交 issue 等待解決外,只能通過(guò)優(yōu)化啟動(dòng)參數(shù)來(lái)解決。

如何找出并解決問(wèn)題

工具

工欲善其事必先利其器,在排查時(shí),我們還是需要一些工具來(lái)幫忙的。

devTool

這個(gè)是今年初出的 Node.js 調(diào)試工具,基于 Electron 將 Node.js 和 Chromium 的功能融合在了一起。操作起來(lái)比 node-inspector 方便,開(kāi)放的 Timeline 功能還是比較實(shí)用的,雖然不是實(shí)時(shí)顯示。
僅需要devtool xxx.js,還可以通過(guò) .devtoolrc 來(lái)進(jìn)行參數(shù)定制,具體見(jiàn)GitHub

heapdump + chrome devTool

這個(gè)是比較傳統(tǒng)的定位內(nèi)存泄漏的組合。heapdump 可以直接在代碼中調(diào)用生成內(nèi)存快照,然后將快照文件導(dǎo)入到 chrome devTool 進(jìn)行分析,之后操作其實(shí)和前者就差不多了。不過(guò),這個(gè)方案和前者有一點(diǎn)區(qū)別就是,前者實(shí)際還是在瀏覽器環(huán)境中,所以生成的內(nèi)存快照會(huì)有一些 DOM 對(duì)象的存在,會(huì)有一定的干擾。而這個(gè)方案,是直接調(diào)用底層 V8 的方法,生成的快照只有 Node.js 環(huán)境中的對(duì)象。

memwatch

這個(gè)可以在代碼里直接使用,實(shí)時(shí)檢測(cè)內(nèi)存動(dòng)態(tài),當(dāng)發(fā)生內(nèi)存泄漏的時(shí)候,會(huì)觸發(fā) ‘leak’ 事件,會(huì)傳遞當(dāng)前的堆狀態(tài),配合 heapdump 有奇效。詳見(jiàn)memwatch。

流程

一、重現(xiàn)問(wèn)題

對(duì)于垃圾回收,V8 引擎有很復(fù)雜的邏輯來(lái)決定什么時(shí)候進(jìn)行回收。很多時(shí)候,當(dāng)我們發(fā)現(xiàn) Node.js 進(jìn)程所使用的內(nèi)存快速增長(zhǎng)的時(shí)候,并不能確定是否是內(nèi)存泄漏導(dǎo)致的,很有可能是程序設(shè)計(jì)問(wèn)題,導(dǎo)致內(nèi)存的不合理利用。只有當(dāng)垃圾回收觸發(fā),未使用內(nèi)存被釋放后,內(nèi)存增長(zhǎng)還在持續(xù),我們才能確定是發(fā)生了內(nèi)存泄漏。

隱藏的內(nèi)存泄漏問(wèn)題,大多是有觸發(fā)條件的,重現(xiàn)問(wèn)題是需要這些條件的,所以我們?cè)谄綍r(shí)寫代碼的時(shí)候,可以將一些重要環(huán)節(jié)的參數(shù)細(xì)節(jié)打印在 log 中,這樣我們?cè)谥噩F(xiàn)問(wèn)題是就不會(huì)摸不著頭腦,亂試一氣。

有了參數(shù)可以用來(lái)重現(xiàn)問(wèn)題,接下來(lái)要確定問(wèn)題。我們要確定,這部分內(nèi)存是否沒(méi)有被 GC 正確釋放。那么問(wèn)題來(lái)了,我們?nèi)绾沃莱绦蜻M(jìn)行了垃圾回收呢?很顯然,等待并不是辦法,我們要主動(dòng)。

在 Node.js 的啟動(dòng)參數(shù)中,提供了暴露手動(dòng)調(diào)用 GC 方法的參數(shù),即--expose-gc。我們用這個(gè)參數(shù)來(lái)啟動(dòng)應(yīng)用后,就可以在代碼中調(diào)用global.gc()手動(dòng)觸發(fā)垃圾回收操作。同時(shí),使用process.memoryUsage().heapUsed獲取進(jìn)程運(yùn)行時(shí)所占用的內(nèi)存。如果 GC 之后,內(nèi)存依然沒(méi)有下降,就可以確定是內(nèi)存泄露了。

二、生成內(nèi)存快照

既然內(nèi)存是問(wèn)題,我們就需要獲取程序運(yùn)行的內(nèi)存快照來(lái)幫助定位問(wèn)題。但內(nèi)存快照并不是隨便打得,是有一定技巧的。

我們至少要生成三次內(nèi)存快照,才能更好的定位問(wèn)題。這三次中又一次要在問(wèn)題出現(xiàn)前生成,之后可以在問(wèn)題持續(xù)的過(guò)程中生成兩次或更多。

為什么要這樣做呢?理解起來(lái)很簡(jiǎn)單。第一次是為了獲取正常情況下的堆棧信息,而在問(wèn)題出現(xiàn)后,堆棧信息一定會(huì)發(fā)生變化,有了第一次的信息,我們才好進(jìn)行后面的比對(duì),過(guò)濾一些無(wú)用的信息。而后兩次的快照,用來(lái)比對(duì)某一對(duì)象的堆棧變化,來(lái)確定是否是有問(wèn)題的對(duì)象。下面會(huì)詳細(xì)應(yīng)用到。

三、定位問(wèn)題

用 devTool 的可以忽略下面的過(guò)程:

打開(kāi) Chrome Devtools ,進(jìn)入到 Profiles 選項(xiàng)卡,點(diǎn) Load 按鈕,加載之前生成的快照。

對(duì)于內(nèi)存快照,有四個(gè)視圖,Summary,Comparison,Containment,Statistics,這里面常用的是前三個(gè)。

在 Summary 視圖中,我們可以看到當(dāng)前快照的全部信息,以及多個(gè)快照之間的信息。在列表里顯示的都是對(duì)象的構(gòu)造函數(shù)名字,可以先忽略被括號(hào)包裹的對(duì)象,優(yōu)先觀察其他的對(duì)象,最后再來(lái)看他們。后面的shallow size表示的是對(duì)象自身的大小,retained size表示的是對(duì)象和它依賴對(duì)象的大小,一般是 GC 不可達(dá)的。

在 Comparison 視圖中,我們可以進(jìn)行多個(gè)快照之間的對(duì)比,這個(gè)用處比較大,如果我們將前兩次快照進(jìn)行對(duì)比,可能比較快速的定位出問(wèn)題的對(duì)象。注意觀察 New、Deleted、Delta,如果是內(nèi)存泄漏的對(duì)象,可能是一直在 New,而沒(méi)有 Deleted。

在 Containment 視圖中,我們可以查看整個(gè) GC 路徑,當(dāng)然一般不會(huì)用到。因?yàn)檎归_(kāi)在 Summary 和 Comparison 列舉的每一項(xiàng),都可以看到從 GC roots 到這個(gè)對(duì)象的路徑。通過(guò)這些路徑,你可以看到這個(gè)對(duì)象的句柄被什么持有,從而定位問(wèn)題產(chǎn)生的原因。值的注意的是,其中背景色黃色的,表示這個(gè)對(duì)象在 Javascript 中還存在引用,所以可能沒(méi)有被清除。如果是紅色的,表示的是這個(gè)對(duì)象在 Javascript 中不存在引用,但是依然存活在內(nèi)存中,一般常見(jiàn)于 DOM 對(duì)象,它們存放的位置和 Javascript 中對(duì)象還是有不同的,在 Node.js 中很少遇見(jiàn)。

更多的操作方法,可以看這個(gè)視頻Memory Profiling with Chrome DevTools和Memory Management Masterclass。還有 Chrome 的文檔Memory Profiling(舊) 和Memory Diagnosis(新)。講的還是很詳細(xì)的。(請(qǐng)自備梯子)

四、解決問(wèn)題

一般在 Javascript 中存在引用而導(dǎo)致內(nèi)存泄漏的情況,是比較好處理的,只需要在使用后及時(shí)的將引用釋放掉即可。

但像《一次 Node.js 應(yīng)用內(nèi)存暴漲分析》所存在的那種內(nèi)存問(wèn)題,是屬于底層機(jī)制的問(wèn)題,如果等不了 bugfix,就只能先通過(guò)一些啟動(dòng)參數(shù)來(lái)優(yōu)化內(nèi)存管理。常用的參數(shù):

--max-old-space-size限制老生區(qū)大小,可以控制內(nèi)存占用的最大值,即使發(fā)生泄漏,也不會(huì)讓內(nèi)存占用保持很高。可以根據(jù)開(kāi)啟進(jìn)程數(shù)以及是否同機(jī)部署來(lái)優(yōu)化。
--gc_global這其實(shí)是個(gè) V8 的 debug flag,讓 GC 永遠(yuǎn)都是 Full GC,使用上會(huì)有一定的性能損耗,根據(jù)應(yīng)用復(fù)雜度不同,損耗不同。

當(dāng)我們找到問(wèn)題,進(jìn)行修復(fù)后,重復(fù)上面的步驟,確認(rèn)問(wèn)題已經(jīng)被解決。有時(shí)可能一次并不能解決問(wèn)題,所以耐心還是很重要的。

實(shí)戰(zhàn)

可以在這里下載使用到的代碼,GitHub,進(jìn)入 memory-leak 文件夾。
我們來(lái)舉個(gè)例子,應(yīng)用上面的步驟排查問(wèn)題,使用 leak-memory 的例子,代碼還有另外一個(gè)例子,可以自己實(shí)踐。

這里我們?yōu)榱朔奖悖覀兪褂昧?devTool。

devTool leak-memory.js

然后在打開(kāi)的界面中進(jìn)入內(nèi)存快照界面,生成第一次快照。當(dāng)控制臺(tái)有輸出后,間隔的生成兩次快照,結(jié)果如下。

我們切換視圖,對(duì)比下三次快照間的區(qū)別,可以看到Foo這個(gè)對(duì)象一直在創(chuàng)建而沒(méi)有被刪除。

我們展開(kāi)Foo,選擇下面的一個(gè)實(shí)例,查看它的 GC path,可以看到它一直被 neverRelease 持有引用(黃色),所以沒(méi)有被釋放,之后就可以進(jìn)行問(wèn)題的處理了。

去掉// neverRelease.splice(index, 1);前的注釋,然后在重復(fù)上面的步驟,你會(huì)發(fā)現(xiàn)內(nèi)存的變化已經(jīng)正常了。

在使用 devTool 時(shí),可以查看運(yùn)行時(shí)的 memory timeline,如果圖像呈現(xiàn)階梯式增長(zhǎng),一般就是存在內(nèi)存泄漏問(wèn)題了。正常的應(yīng)用曲線會(huì)類似于鋸齒,如圖:

總結(jié)

內(nèi)存泄漏問(wèn)題的定位,經(jīng)驗(yàn)很重要,但有了良好工具的輔助,可以節(jié)省很多時(shí)間。如果懶得自己一步步的操作,可以接入alinode,這個(gè)可以幫助你很方便的生成快照等運(yùn)行時(shí)數(shù)據(jù),并有一定的分析輔助,還是方便的。

你可能看到很多內(nèi)存分析的文章會(huì)有一些圖來(lái)表示內(nèi)存的增長(zhǎng),可以使用 python 來(lái)快速生成相關(guān)的圖片,使用matplotlib.pyplot這個(gè)包。

參考

memory-diagnosis
Memory Profiling with Chrome DevTools
Simple Guide to Finding a JavaScript Memory Leak in Node.js
A tour of V8: Garbage Collection

轉(zhuǎn)載自:http://taobaofed.org/blog/2016/04/15/how-to-find-memory-leak/
作者:凌恒

總結(jié)

以上是生活随笔為你收集整理的如何定位 Node.js 的内存泄漏的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。