最近在學推薦系統,萌生一個從頭實現一個推薦系統的想法。說作就開始着手,第一步先寫一個視頻爬蟲。html
在網上找了一個有網頁的版的視頻聚合源,用nodejs+jsdom快速搭建了一個spider,爬取過程發現用併發的請求個數很差控制,太多容易把源網站爬掛了,就引入了async.parallelLimit和async.queue來作併發請求控制;另外看網上資料jsdom資源佔用比較多,cheerio更輕便,便切換到cheerio。node
但運行一段時間以後發現內存漲的很是快,像是存在內存泄露問題。git
遇到問題不要着急,先進行下邏輯分析,再經過工具去逐步確認本身的假設或找到更多可疑的地方,兩種方式不斷交叉最終確認問題。github
問題:爬蟲啓動以後內存快速增加。segmentfault
--max_old_space_size=512 --gc_interval=100 --expose_gc
,而後在代碼裏面定時主動調用global.gc()
,但內存仍是飈的很快。node --trace_gc spider.js | grep Mark-sweep
數組
發如今直到415行以後添加continue,內存又開始漲得很厲害了。因此能夠定位是415行這句代碼致使了內存泄露。415行就一個tvLink的賦值爲啥會致使內存泄露呢?處於好奇就這414行打印了一句閉包
console.log("tvLink=", tvLink)
併發
神奇的事情發生了,再次跑的時候內存又不暴漲了,內存泄露問題解決了。諮詢了下同事super大神,思路切換到既然知道videoData沒有被釋放掉,那就看看是誰retain着他?切換到Chrome的Profiler,能夠點擊字符串看到誰retain着這些字符串。dom
能夠看到href是一個sliced string,記得以前看一篇文章說過sliced string致使的內存不釋放的問題,頓時明白了,sliced string顧名思義就是他不實際存儲字符串,而是存儲他在父字符串的startOffset和len 因此href其實就是videoData的sliced string,這也是爲啥videoData不能在循環的時候雖然不用了但仍是不能被釋放。但只要console.log就能迫使sliced string提取出確切的值,既然提取出值後面也不必再存儲成sliced string,因此內存泄露的問題也就解決了。附錄還有一篇super大神寫的SliceString的文章。 能夠理解sliced string實際上是爲了優化字符串使用,但在我這個特定場景確會產生內存不能被快速釋放的問題。準確的講這不算是一個內存泄露的問題,而是一個內存堆積的問題。那有啥辦法能夠規避sliced string引入的問題呢?經同事建議,只要對這個字符串進行操做就能flatten sliced string,好比輕量的parseInt,而console.log其實也是一種,但不建議。async