一次网站崩溃的解决方案

624 阅读5分钟

记一次网站崩溃解决方案

背景

新入公司,维护一个老项目, 项目是一个供公司内部使用的工具,开始做项目时,公司对于前端不重视,导致前端项目......(不忍直视),拿着工资得干活,活还是得干.
刚接手时,领导说项目问题比较多, 有些 bug 已经存快 2 年了,还未决,我当时想着并未多想,做业务的,没有大佬组织的情况下,哪个项目不烂?直到我仔细看了项目,打破了我的认知...

项目

项目是一个客户端项目.由 c#提供外壳, 内嵌网页. 而双方通信的方式也很简单, 向 window 上挂一个方法, 外壳就可以调用网页的方法.
采用的技术栈有 react,reudx redux-saga, antd, event. 看下代码 code.png 稍微解释下. 项目中并未使用 react-redux, 没错, 确是没用. 在 mount 中订阅, 在卸载的时候,删除 redux 订阅(删除订阅,还是我给改的); 还有添加一些事件. 由于在客户端中使用, 监听了一些快捷键,让项目使用更方便.以及使用了一些发布订阅,可以在外部非常方便的修改组件的状态(这个用起来,非常爽,维护起来可能要骂人).

错误定位

先看一个图, 当页面长时间操作后, 会发生崩溃, 打开 performance monitor(more tools => performance monitor)

可以看到内存使用达到 1G 左右. 什么项目能使用 1G 左右的内存,你把 chrome 浏览器重写一遍, 也没这么大哪..

下面开始定位问题, 打开 memory 面板, 截取多次快照做比对.就用 heap snapshot 即可.
memeorysnap1.png

错误定位

将黄圈圈的地方选择 comparsion 比对. 即比较和上次快照不同的地方(新增的变量和删除的变量都有记录);

这个图怎么看呢. 其实最主要的是看 string. 因为不管是对象和数组, 最终里面使用的都是 string, 只要观察 string 的变化即可.

介绍一下里面对应的字段.
new: 新增的字符串.
deleted: 删除的字符串.
delta: 删除或者增加的字符串
allocSize: 分配的内存.
freedSize: 释放的内存.
sizeDelta: 增加或者减少的内存
主要看 allowSize 和 freeSize.变化是否正常.
我觉得定位内存泄漏比较麻烦的一点是, 需要从茫茫的数据中,查找哪些是泄漏的变量,哪些是新增的变量,我这个项目中, string 变量大约有 70w 个左右,看全是不可能的, 只能随机抽查看. 随机选中一个字符串查看一下

memeorysnap2.png 黄色包裹的部分是当前字符串的引用的详细信息.最终可以在 window 中访问到
每一条信息后面都有@666969,@66611 等数字, 这个@xxx 表示字符串或者对象的引用地址. 按 ctrl+f 并输入@xxxx 可以查到对应对象的信息.
从图片中可以看到最外层的对象应用信息是@5591, 在朝上就是系统内置的信息,无法查看.

那到底该字符串到底是新增的字符,还是泄漏的字符呢?
主要看 allocSize, 如果有值, 表示当前快照比上次快照新增的对象(无法判断是否泄漏)
如果 sizeDelta 有值,表示此次快照比上次快照的减少的内存(比如同一个对象, 给它新赋值后,上次的值要被销毁);

以下截图是我的定位问题的三张图

tasktoo1.png


tasktoo2.png



tasktoo3.png 上面三个图, 都是同一个字符串, 黄色重点区域 xxxx in MyTask @xxxx, 其中,MyTask 是组件实例(页面中只会调用此组件 1 次), 而图中每个实例的的地址都不一样,有多个组件实例,这表示当操作过后, 组件实例泄漏了.

在朝下看, 会有一个_events in EventEmitter@69679, 而这个 event 是项目中使用的发布订, event 对象是全局唯一的, 即使组件卸载,event 对象也不会发生改变.所以上图中的 EventEmitter 总是同一个地址, 无论页面操作多少次, 都是同一个.

解决

很快就想到,是因为项目中使用了发布订阅引起的问题, 在组件挂载时订阅事件,在组件卸载时, 却未删除订阅引发了此问题. 由此改起来就比较简单了.

个人感觉是一个简单的问题,只是从未想起来. 在解决问题的过程中,更熟悉了 memory 工具的使用. 像写代码过程中,碰到一些监听事件, setTimeout setInterval 都会记起来去清除, 可能 event 库用的少, 未想起来删除订阅,从意识上就没考虑过要去删除发布订阅模式中订阅的东西.(为什么要使用发布订阅,我 xxxx);

在业务项目中,在迫不得已的情况下,还是别使用发布订阅. 我在维护项目的过程中深有感触,用起来非常爽,但是项目到处都是监听与订阅, 当一个事件被触发, 去找触发源,是一个灾难,特别影响心情. 下次要是在碰到在业务中使用发布订阅,大伙给我点勇气, 见一个打一个~~~