前端vue项目内存泄漏排查总结

名词

内存泄漏(Memory Leak):不再用到的内存,没有及时释放;
内存溢出(Out Of Memory):应用系统中存在无法回收的内存或使用的内存过多,最终使得程序运行要用到的内存大于能提供的最大内存。

案例背景

  1. 项目由vue-element-admin改造,二次封装了element-ui,组件套的有点深;
  2. 系统上线一段时间后,业务频繁反馈页面崩溃;
  3. 项目使用了keep-alive页面缓存,移除的话没问题,但因业务需要不能移除:
内存溢出.png

内存泄漏排查方法 - 谷歌开发者工具 Chrome Devtools

  1. performance打快照;

a. 在面板勾上内存,点击垃圾桶手动进行一次垃圾回收,避免干扰,然后点击第一个灰色实心圆圈图标开始录制;
b. 操作页面;
c. 再点击垃圾桶手动进行一次垃圾回收,然后点击停止录制按钮;

内存泄漏排查 - performance.png
  1. performance monitor性能监控器实时查看变化,同样要记得进行垃圾回收;
内存泄漏排查 - performance-monitor 性能监控器.gif
  1. memory打堆快照,页面操作前拍摄一次,操作拍摄一次,然后筛选快照之间分配的对象;
内存泄漏排查 - memory 打堆快照.gif

操作前后快照对比 - vue组件.png
  1. memory时间轴查看未释放内存;

a. 点击垃圾桶手动进行一次垃圾回收,再点击圆圈进行录制;
b. 操作页面;
c. 再点击垃圾桶手动进行一次垃圾回收,然后点击圆圈停止录制;

内存泄漏排查 - memory 时间轴查看未释放部分.gif

排查

  1. 使用谷歌开发工具面板performancememory排查,打快照,发现详情页内存一直累积,无法释放;
  2. 列表页和详情页引用了公共组件(注意import引入的资源在内存中实质上是同一个对象);
  3. memory排查分析,发现累积的都是些vue组件;
vueComponent.png
操作前后快照对比 - vue组件的name.jpg
分离的dom没有回收.png
  1. 注意组件的name与路由的name保持一致且唯一,不然用keep-alive无法正常缓存(通过componentOptions.Ctor.options.name || componentOptions.tag决定include包含的组件缓存);
function getComponentName (opts) {
  return opts && (opts.Ctor.options.name || opts.tag)
}

解决方案

  1. 缓存的页面走keep-alive,不缓存的把router-view移到外面,即:
  <transition name="fade-transform" mode="out-in">
    <div>
      <keep-alive :include="cachedViews" :max="10">
        <!-- 匹配三级路由,并缓存 -->
        <router-view v-if="!$route.meta.noCache" :key="key" />
      </keep-alive>
      <router-view v-if="$route.meta.noCache" :key="key" />
    </div>
  </transition>
  1. 比较粗暴的方法:主动销毁组件,详情页离开(销毁)之前,递归把组件的componentInstanceelm.innerHTML移除,即把组件的联系断开,以便浏览器回收(GC):
/**
 * @name 深度销毁组件
 * @description 用于没有使用 keep-alive 缓存的页面(详情页)
 */

export default {
  beforeDestroy() {
    async function destroyDeep(vnode) {
      let vnodes
      if (vnode.children || vnode.componentInstance?._vnode?.children) {
        vnodes = vnode.children || vnode.componentInstance._vnode.children
        for (const vn of vnodes) {
          destroyDeep(vn)
        }
      }

      vnode.componentInstance?.$destroy()
      setTimeout(() => {
        vnode.componentInstance = undefined
        vnode.elm.innerHTML = ''
      }, 0)
    }

    destroyDeep(this._vnode)
  }
}
  1. vuevue-template-compiler都升级到2.6.13版本;

但有个情况依然有问题,就是两个不同列表(缓存)的详情页(不缓存)之间切换,详情页引用同一个详情组件的时候内存一样不释放。

issues keep-alive内存溢出
issues 源码修改commit
issues 源码改动部分

keep-alive组件部分源码:

vue@2.6.10

  if (cache[key]) {
    vnode.componentInstance = cache[key].componentInstance;
    ...
  } else {
    cache[key] = vnode;
    ...
  }

vue@2.6.13新增的cacheVNode方法

  if (vnodeToCache) {
    var tag = vnodeToCache.tag;
    var componentInstance = vnodeToCache.componentInstance;
    var componentOptions = vnodeToCache.componentOptions;
    cache[keyToCache] = {
      name: getComponentName(componentOptions),
      tag: tag,
      componentInstance: componentInstance,
    };
    ...
    this.vnodeToCache = null;
  }

vue@2.6.13mountedupdated都执行了this.cacheVNode()

这里this.vnodeToCache = null,把vnode的引用断开了,而旧版本的cache[key] = vnode是没有做这个操作。

这里的缓存只用到三个参数:组件的nametag、实例componentInstance,如果一直保持引用,会导致缓存无法释放。

keep-alive是一把双刃剑,使用好能提高用户体验,没使用好效果却相反。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,287评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,346评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,277评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,132评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,147评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,106评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,019评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,862评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,301评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,521评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,682评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,405评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,996评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,651评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,803评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,674评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,563评论 2 352

推荐阅读更多精彩内容