项目是利用vue框架开发的公司内部的异常监控系统,用于显示java程序运行时的异常信息,包括执行堆栈、代码、变量等信息显示。vue
在测试过程当中,部门同事反映:在不一样的异常信息之间屡次切换,会致使网页崩溃。在案发现场打开 chrome 的任务管理器,看到这个页面内存占用已经达到了9.7G,初步怀疑页面存在泄漏。java
能够看到Nodes、Listeners、JS Head(memory) 的阶梯式增加,中间的增加节点对应就是每一次操做。很显然这个操做会引发内存的持续增加,最终发生内存溢出也是瓜熟蒂落了。web
在动手以前,我已知的信息有:chrome
- 变量引用DOM
- 子级DOM不能释放,会致使父级也不会被释放
- 若是DOM能被正常GC, 对DOM的事件监听器也会自动移除
嗯,内容有点多,可是也还算清晰:api
- Distance:到root的引用距离
- Shallow size:对象自己的大小,不包含它引用的数据的大小
- Retained size:对象自身以及全部引用的大小,就是对象总共占用的内存 (若是它引用的对象不被其余不可回收的对象引用的时候。用google开发者网站的描述叫:将对象自己连同其没法从GC根到达的相关对象一块儿删除后释放的内存大小)
该操做的核心代码大体是这样的 架构
![]()
主要功能是,每次执行setEvent,都将 this.exception 指向新的实例,并交给页面进行数据展现,而以前被this.exception引用的对象,应该被释放。
从新收集新的内存快照信息框架
找出差别:将视图改成差别视图chrome-devtools
从图上能够看到在步骤1以后,出现了不少新增的对象,可是删除的对象是0。 ![]()
以 Exception对象为例,按照步骤1的代码逻辑,新对象创建,旧对象被释放,Delta 应该为零。因此能够明确知道,这里是一个问题。不过这里点开查看变量的引用详情,并没获得太多有用的信息,只知道被哪一个 vue component 引用了,可是component 太多,不太好定位。函数
查看 Listenters, 我看到的画风基本是这样的:工具
跟预期的结果一致,都是因为一些 Nodes 没有被释放致使的。不过确实没有获得太多方便分析的信息。 ![]()
另外查看Nodes相关的信息,搜索 Detached, 能够看到一些 Detatched HTMLDivElement等等相似的对象,也就是在内存中可是没有在页面进行渲染的元素
我找了一个detla比较小的、节点功能也清晰(就是用来在页面中进行代码高亮的元素)的 Detatched HTMLPreElement 进行分析:
能够看到实际引用关系为 div <= div <= div <= vue component <= var-hover <= events <= ... $platform.event...
在这里 $platform.event 是由平台 + 模块的架构设计中,平台提供的事件 api, 用于全局的事件通讯。
最终将以上引用关系进行翻译:由平台提供的事件 $platform.event (全局,绑定的事件函数不会被自动释放),绑定了一个叫作 var-hover 的事件 => var-hover 的事件函数中引用了一个 vue-component => component 的$el属性 引用了某个Dom => Dom的父级被子级引用致使不能被GC。
能够看看 var-hover 的代码:
![]()
var-hover 绑定了一个匿名函数(基本上也能够知道,没有给这个事件没有写过解绑操做),而后匿名函数中使用了 this, 也就是当前 vue component,这也致使了被这个 component 引用的对象都不能被GC。
因此祸根基本上找到了,接下来要作的就是:修复 -> 从新验证
这就是$platform.event 的实际实现 ![]()
var-hover的事件绑定以下
![]()
移除了 beforeDestroy 钩子,业务层看起来也好多了。
这里的每次峰值,就是刚执行进行操做时进行内存分配的结果,以后每次执行,并无出现内存及 Nodes, Listensers 的累积 ![]()
再次对比一下修正以前的性能分析结果
![]()
可怕的楼梯。。。
多了一个 StackFrameVar 以及一些为了呈现这个 StackFrameVar 对象多出来的一些EventListener、Observer等,这是因为两次呈现的数据自己不同致使的,属于正常状况 ![]()
Exception、 等不少对象的 Delta 已经为0了(按 Delta倒序排列的)
以上分析图是写这篇文章过程当中,回写部分代码以后实时分析的,相对而言没有实际调试时处理得那么细致。实际调试过程当中还作了其它操做:
经过以上方式是为了提供一个彻底纯净可控的分析环境。