前言
内存泄漏对于前端的影响通常都是发生在客户端上,而且基本上现代浏览器也会做好保护机制,一般自行刷新之后都会解决。但是,一旦后端内存泄漏造成宕机之后,久而久之整个服务器都会受影响,甚至瘫痪。虽然可以用pm2设置一个内存值上限,超过自动重启,但是知道有问题却一直未解决的感觉太难受了。还是要找到问题的根源,为什么会发生内存泄露?哪个变量发生了内存泄露?
一、什么是内存泄露
1、内存的生命周期
- 分配我们所需要的内存
- 用分配到的内存做点什么
- 不用时进行释放回收
一个内存的生命周期是系统先借给我们一块内存,我们用这块内存搞事情,用过之后再还给系统。而内存泄漏就是这块内存用过之后却不还给系统了。
刚刚提到内存的生命周期,最后一个阶段是不用时进行释放回收,那我们的系统如何判断这块内存不用了?
2、垃圾回收机制
js有个垃圾回收机制,会按照固定的时间间隔,周期性的找出不再继续使用的变量,然后释放其占用的内存。语言引擎有一张”引用表”,保存了内存里面所有的资源(通常是各种值)的引用次数。如果一个值的引用次数是0,就表示这个值不再用到了,就可以将这块内存释放。
内存的分配以及内存的回收完全实现了自动管理,我们不用操心这种事情。还怎么还会出现内存泄漏的问题呢? 为什么我们不用这块内存了,但是引用次数不是0呢?主要是以下几种情况
- 闭包
- 全局变量不会被当成垃圾回收
- 被遗忘的定时器和回调函数 在定时器完成工作的时候,手动清除定时器。
二、内存泄漏排查以及填坑
1、情境
Memory Usage 从 232MB 过了 20h 后稳步提升到 1.17G,妥妥的内存泄漏。然而运维的监控是按整个容器颗粒度的,知道了内存泄漏并不能直接知道问题所在,但是缩小了问题域,导致服务器内存泄漏可能有几种原因:
- 代码编写不当。
- 使用的外部模块有内存泄漏。
- 使用的外部模块的C++部分有内存泄漏。这个最难排查。
- 生产速度大于消费速度,堆积起来,这个一般涉及到IO。比如数据库查询、外部api调用、日志打印写磁盘等。
2、背景
首先排查代码编写不当,项目是 koa + (vue|vue ssr)。看了一些文章,排查 node.js 的内存泄漏,一般是用工具抓内存快照,然后进行分析。有很多工具:devtool、memwatch、memwatch-next、heapdump,试了memwatch是个死项目,很久没维护了无法安装,试了memwatch的下一个版本memwatch-next,也无法安装。试了heapdump,虽然安装的过程比较坎坷,但至少安装成功了!然后就是抓快照,过一段时间后,用快照传到chrome的Memory进行分析snapshot。
另外pm2 monit
可以实时查看容器的内存使用量、CPU等。由于项目 vue ssr 的页面不多,在本地访问其中几个页面时,可以看到 pm2 monit 上的内存使用率瞬间多了十几兆,但关闭页面后,内存并没有回收,然后就code review 对应的页面,查看是否有内存泄露的代码进行修改。
3、vue ssr 可能造成泄漏的位置
1)SSR 的生命周期里只有 beforeCreate 和 created
2)路由守卫处
跟生命周期不同,所有的 route guard 都会在 SSR 运行。分别是
- beforeEach
- beforeRouteUpdate
- beforeEnter
- beforeRouteEnter
- beforeResolve
- afterEach
- beforeRouteEnter
3) Global Mixin
global mixin 会给每个 Vue 实例一个拷贝,而不是引用
三、总结
修改后的内存监控曲线情况有所好转。