最近在学推荐系统,萌生一个从头实现一个推荐系统的想法。说作就开始着手,第一步先写一个视频爬虫。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