記錄一次定時器及閉包問題形成的內存泄漏

前言

這是一篇記錄工做上所遇到的關於內存泄漏問題及如何解決的文章。前端

問題描述

先大概描述一下個人問題:vue

技術棧: 框架:vue 組件庫: ant design of vue 圖表:g2.web

項目需求: 在下圖的看板中, 須要每20秒切換一次下面的兩個小藍點, 以達到切換並刷新圖表的功能.前提是不用web socket等輪詢工具.chrome

下圖的組件結構: switch-flex-chart 組件由 flex-chart 組件和 switch-icon-card 兩個組件組合而成, 我經過給switch-icon-card 組件(也就是兩個小藍點) 添加一個定時器, 每20秒改變一下index,而且回調給父組件 (switch-flex-chart) 而後刷新並改變 flex-chart 組件裏的圖表數據.瀏覽器

img

形成的問題: 剛開始還好,可是若是時間久了,就會形成頁面很是的卡頓, 但是在代碼中, 我在生命週期-銷燬階段的裏每次都會把定時器給清除並置爲nullbash

1、定時器要及時清除

這裏輪詢的功能我沒有使用setInterval,而是使用了兩個setTimeout框架

<template></template>

<script>
export default {
  created() {
        this.init()
  },
  mounted() {},
  data() {
    return {
        duration: 20000, // 多久刷新一次
        _timer: null // 定時器
    }
  },
  methods: {
    init () { // 初始化
        if (this.duration > 0) {
          this._timer && clearTimeout(this._timer)
          this._timer = null
          this.polling()
        }
    },
    autoPlay () { // 切換的函數
        ...
    },
    polling () {
        this._timer = setTimeout(() => {
          this.autoPlay()
          this._timer && clearTimeout(this._timer)
          this.polling() // 循環調用自身
        }, this.duration)
    }
  }
}
</script>
複製代碼

如上面的代碼,我在每次輪詢polling的時候,都會使用clearTimeout來清除一下定時器,可是我發如今你切換到其餘頁面的時候,定時器仍是在默默地執行着,因而我想到了在組件每次銷燬的時候也必須把定時器也銷燬:socket

destroyed() { // 生命週期-組件銷燬
    this._timer && clearTimeout(this._timer) // 先使用clearTimeout
    this._timer = null // 最好將定時器也設置爲null
}
複製代碼

2、圖表數據及時清空

上面的方式解決了切換頁面定時器還在運行的問題,可是仍是沒有解決頁面會很卡頓的狀況。正常來講就算定時器跑的再久也不會有這個問題。因此確定是有哪裏形成了內存泄漏。函數

爲了驗證是否是這個緣由,我打開了chromeF12控制檯。而後找到了Memory工具

memory

在這裏你能夠記錄JavaScript對象的堆快照,查找到內存泄漏。

此時我每10秒錄製一個快照,分別對應Snapshot1-3,發現JavaScript對象的總大小一次比一次大,要不了一會個人瀏覽器就卡的不行了。

堆快照

首先我想到的是否是圖表內容沒有清空,因此我在每次繪製g2圖表的時候先執行了一遍g2內置的方法destory()進行銷燬。

<template></template>

<script>
export default {
  created() {
        this.init()
  },
  mounted() {},
  data() {
    return {
        chart: null
    }
  },
  methods: {
      init () {
        if(this.chart){ // 若是存在的話就銷燬圖表再從新生成
          this.chart.destroy()
        }
        this.chart = new G2.Chart({
            ...
        })
      }
  }
</script>
複製代碼

覺得大功告成的我再次打開瀏覽器,發現內存泄漏的問題並無解決。因而我想這個圖表對象是否是也要像定時器同樣,在銷燬的時候釋放內存呢?

動手試試:

destroyed () {
    this.chart.destroy()
    this.chart = null
}
複製代碼

處理完以後,我再次錄製了三個快照,此次JavaScript對象的大小沒有再增加了,而且我頁面掛在那半個小時也絲毫沒有卡頓的跡象。

後記

雖然問題解決了,但我還不是很清楚,爲何在初始化的時候使用this.chart.destroy()不可以釋放內存,而非要使用設置成null的方式。Googleg2destory()以後也是蹦出了一堆LOL裏的G2...

在此也要感謝 OBKoro1 的傾囊相助。

參考文章:

談一談我在前端開發的時候遇到的過的內存泄漏

JS高程中的垃圾回收機制與常見內存泄露的解決方法

相關文章
相關標籤/搜索