淺析Echarts圖表渲染導(dǎo)致內(nèi)存泄漏的原因及解決方案
一. 引言
在今年某個可視化大屏項目中,出現(xiàn)了一個問題。項目在運行一段時間后,頁面出現(xiàn)了崩潰,而且是大概運行幾天之后,因為大屏項目是部署到客戶現(xiàn)場大屏,長時間運行不關(guān)閉。報錯問題如下圖所示:

由于這個大屏頁面是使用 Echarts 圖表渲染的,整個頁面就一個渲染地圖的功能,沒有多少耗費內(nèi)存的操作。而 Echarts 作為一款強大的數(shù)據(jù)可視化庫,被廣泛應(yīng)用于各種項目中,因此 Echarts 的穩(wěn)定性也是經(jīng)過廣大開發(fā)者驗證的,不會出現(xiàn)明顯的 bug 問題。
然而,我們也知道,如果頁面隨著圖表數(shù)量的增加和動態(tài)更新的需求,我們必然會考慮 Echarts 圖表渲染導(dǎo)致的內(nèi)存泄漏問題。因此,上面的問題我們按照這個猜想來往下走,認為 Echarts 圖表渲染導(dǎo)致了內(nèi)存泄漏,那么 Echarts 的什么操作會導(dǎo)致內(nèi)存泄漏、甚至到頁面崩潰呢?
本文將深入分析這一問題,并提供解決方案。
二. 問題背景
猜測原因
當(dāng)我們使用 Echarts 來渲染大量圖表時,會發(fā)現(xiàn)頁面的內(nèi)存占用不斷增加,最終導(dǎo)致頁面卡頓甚至崩潰。這是由于 Echarts 在圖表渲染過程中產(chǎn)生的內(nèi)存泄漏導(dǎo)致的。
而奇怪的是,自己在本地中測試的時候,從未有過崩潰的現(xiàn)象?但是在客戶現(xiàn)場就出現(xiàn)了這種問題。

內(nèi)存占用檢測
為了能夠?qū)崟r監(jiān)測網(wǎng)站運行過程中,我們在 Chrome 瀏覽器打開了內(nèi)存性能分析,便于實時查看內(nèi)存占用情況,發(fā)現(xiàn)運行內(nèi)存占用并不是太大,我們監(jiān)測過一段時間后發(fā)現(xiàn),確實內(nèi)存有時在穩(wěn)定的增加,但是這點內(nèi)存占用還不至于導(dǎo)致系統(tǒng)崩潰。

內(nèi)存泄漏原因分析
嵌套事件綁定:在 Echarts 圖表中,每個圖表都會綁定各種交互事件。如果我們正確地解綁這些事件,則會導(dǎo)致事件監(jiān)聽器無法垃圾回收,從而造成內(nèi)存泄漏。
大量的圖表實例:如果我們在動態(tài)更新圖表的過程中,每次都創(chuàng)建新的圖表實例而不銷毀舊的實例,就會導(dǎo)致內(nèi)存占用不斷增加。
定時器未清理:如果我們在更新圖表的過程中使用了定時器來控制刷新頻率,但未能正確清理這些定時器,就可能導(dǎo)致內(nèi)存泄漏。
三. 第一輪解決方案
因為可視化大屏項目代碼不是我直接寫的,我當(dāng)時只不過是被臨時抽過去提過幾個建議而已,所以并沒有深入的研讀項目代碼,所以這一輪解決方案可以定義為是在浪費時間,不過也并不是一點經(jīng)驗沒有,一起來看一下吧。
初步解決
為了解決 Echarts 圖表渲染導(dǎo)致的內(nèi)存泄漏問題,我們采取以下措施:
在綁定事件監(jiān)聽器時,要確保正確解綁,可以使用 Echarts 提供的
off方法來取消事件定。在圖表組件銷毀之前,務(wù)必使用
dispose函數(shù)銷毀圖表實例,以確保釋放內(nèi)存。在動態(tài)更新圖表時,盡量復(fù)用現(xiàn)有的圖表實例,而不是每次都創(chuàng)建新的實例。可以使用 Echarts 提供的
setOption方法來更新圖表數(shù)據(jù)和配置。如果使用了定時器來控制圖表的刷新頻率,必須在組件銷毀前清定時器,可以使用
clearInterval或clearTimeout來停止定時器。正確管理圖表組件的生命周期,盡量避免不必要的渲染和更新操作。
初步驗證
按照以上的初步解決方案,我們對流程進行了初步優(yōu)化,主要進行了事件監(jiān)聽器的綁定與解綁優(yōu)化、銷毀定時器、dispose 函數(shù)銷毀圖表實例等操作。
等這些優(yōu)化操作完成后,我們又部署到自己的系統(tǒng)進行初步驗證,發(fā)現(xiàn)內(nèi)存占用確實變的小了,但是等到運行長時間來看,內(nèi)存還是有上升的趨勢。因此我斷定,沒有找到根本原因解決。
果然,運行了大概有五天的時間,瀏覽器還是頂不住了,系統(tǒng)再一次不工作了。
四. 第二輪解決方案
可能原因
由于這個大屏項目是其他同事開發(fā)的,具體的代碼我并不太清楚。因此,先前只是提供了一些建設(shè)性的建議,沒想到同事修改完成后還是沒有解決了根本問題。所以可能必須要完全讀懂項目的代碼才能找到根本原因。
果然,看了幾遍代碼后發(fā)現(xiàn)了一些端倪,頁面部署上之后是不會再進行操作了,數(shù)據(jù)會自動定時刷新,地圖數(shù)據(jù)也會自動刷新,因此,問題就出現(xiàn)在這了。
猜想
多次調(diào)用 Echarts.init,項目中這一段代碼寫的確實有問題,寫了個定時器,每次刷新數(shù)據(jù)時,都需要調(diào)用 Echarts init,并且銷毀時 clear 和 dispose 方法使用不當(dāng)造成,定時器循環(huán)重繪 Echarts 圖表導(dǎo)致內(nèi)存一直升高,最終導(dǎo)致了瀏覽器崩潰。
解決方案
通過 Echarts init 方法創(chuàng)建 Echarts 實例,如果代碼沒有做優(yōu)化,echarts 實例就會越來越多,占用大量內(nèi)存,有以下兩種方法可以避免這種情況:
第一種:使用 Echarts init 之前先判斷是否存在實例
const chart = Echarts.getInstanceByDom(document.getElementById(dom));
if (chart === undefined) {
chart = Echarts.init(document.getElementById(dom));
}
第二種:如果 Echarts 存在,先 dispose 銷毀后,再調(diào)用 init
const chart = Echarts.getInstanceByDom(document.getElementById(dom));
if (chart) {
Echarts.dispose(chart);
}
chart = Echarts.init(document.getElementById(dom));
驗證
使用上述的代碼進行優(yōu)化,再結(jié)合第一輪的代碼優(yōu)化后。系統(tǒng)又重新部署了,監(jiān)測系統(tǒng)內(nèi)存狀態(tài),初始的內(nèi)存占用大小和之前的相差不大。不過觀察一段時間后,內(nèi)存沒有持續(xù)升高的趨勢,還算比較穩(wěn)定。又這樣運行了大概有一周左右,發(fā)現(xiàn)內(nèi)存占用仍然穩(wěn)定,系統(tǒng)也沒有出現(xiàn)過崩潰的問題。因此可以斷定,應(yīng)該是優(yōu)化好了。
五. 總結(jié)
Echarts 圖表渲染導(dǎo)致的內(nèi)存泄漏問題是我們在使用 Echarts 時經(jīng)常遇到的挑戰(zhàn)之一。
在 Echarts 做圖表開發(fā)時,避免內(nèi)存泄漏的幾點操作主要有以下幾個方面:
多次調(diào)用 Echarts.init 會導(dǎo)致內(nèi)存泄漏,應(yīng)當(dāng)在恰當(dāng)時機銷毀已經(jīng)存在的 Echarts 實例,使用 clear() 和 dispose() 手動清理,區(qū)別在于:
- clear()不會銷毀實例,只是重新繪制圖形,
- dispose()會銷毀實例,需要重新構(gòu)建 ECharts 對象
在動態(tài)更新圖表時,盡量復(fù)用現(xiàn)有的圖表實例,而不是每次都創(chuàng)建新的實例。可以使用 Echarts 提供的
setOption方法來更新圖表數(shù)據(jù)和配置。在綁定事件監(jiān)聽器時,要確保正確解綁,可以使用 Echarts 提供的
off方法來取消事件。
通過深入分析內(nèi)存泄漏的原因,并采取相應(yīng)的解決方案,我們可以有效地解決這一問題,確保應(yīng)用的穩(wěn)定性和性能。
以上就是淺析Echarts圖表渲染導(dǎo)致內(nèi)存泄漏的原因及解決方案的詳細內(nèi)容,更多關(guān)于Echarts圖表渲染導(dǎo)致內(nèi)存泄漏的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
js完美解決IE6不支持position:fixed的bug
關(guān)于IE6,雖然它已被微軟拋棄很久了,但是由于大天朝的特殊行情(盜版)對于前端工程師來說,解決IE6兼容position:fixed的問題顯得很重要。特別是你需要用到頭尾懸停調(diào)用的時候2015-04-04
JavaScript實現(xiàn)定時隱藏與顯示圖片的方法
這篇文章主要介紹了JavaScript實現(xiàn)定時隱藏與顯示圖片的方法,可實現(xiàn)javascript定時關(guān)閉圖片的功能,涉及javascript針對頁面元素屬性定時操作的相關(guān)技巧,需要的朋友可以參考下2015-08-08
獲取Javscript執(zhí)行函數(shù)名稱的方法
獲取Javscript執(zhí)行函數(shù)名稱的方法...2006-12-12
使用Layer組件彈出多個對話框(非嵌套)與關(guān)閉及刷新的例子
今天小編就為大家分享一篇使用Layer組件彈出多個對話框(非嵌套)與關(guān)閉及刷新的例子,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-09-09
JavaScript高級函數(shù)應(yīng)用之分時函數(shù)實例分析
這篇文章主要介紹了JavaScript高級函數(shù)應(yīng)用之分時函數(shù),結(jié)合實例形式分析了javascript通過合理分時函數(shù)應(yīng)用避免瀏覽器卡頓或假死的相關(guān)操作技巧,需要的朋友可以參考下2018-08-08
BootStrap表單驗證 FormValidation 調(diào)整反饋圖標位置的實例代碼
這篇文章主要介紹了BootStrap表單驗證 FormValidation 調(diào)整反饋圖標位置的實例代碼,需要的朋友可以參考下2017-05-05

