JS造成記憶體洩漏的幾種情況例項分析
本文例項講述了JS造成記憶體洩漏的幾種情況。分享給大家供大家參考,具體如下:
介紹:
js中的記憶體垃圾回收機制:垃圾回收器會定期掃描記憶體,當某個記憶體中的值被引用為零時就會將其回收。當前變數已經使用完畢但依然被引用,導致垃圾回收器無法回收這就造成了記憶體洩漏。傳統頁面每次跳轉都會釋放記憶體,所以並不是特別明顯。
Vue單頁面應用中:Web App 與 傳統Web的區別,因為Web App是單頁面應用頁面通過路由跳轉不會重新整理頁面,導致記憶體洩漏不斷堆積,導致頁面卡頓。
洩漏點:
1.DOM/BOM 物件洩漏
2.script 中存在對DOM/BOM 物件的引用導致
3.Javascript 物件洩漏
程式碼關注點:
1.DOM中的addEventLisner 函式及派生的事件監聽, 比如Jquery 中的on 函式, vue 元件例項的 $on 函式,第三方庫中的初始化函式
2.其它BOM物件的事件監聽, 比如websocket 例項的on 函式
3.避免不必要的函式引用
4.如果使用render 函式,避免在html標籤中繫結DOM/BOM 事件
Vue如何處理:
1.如果在mounted/created 鉤子中綁定了DOM/BOM 物件中的事件,需要在beforeDestroy 中做對應解綁處理
3.如果元件中使用了定時器,需要在beforeDestroy 中做對應銷燬處理
4.模板中不要使用表示式來繫結到特定的處理函式,這個邏輯應該放在處理函式中?
5.如果在mounted/created 鉤子中使用了$on,需要在beforeDestroy 中做對應解綁($off)處理
6.某些元件在模板中使用 事件繫結可能會出現洩漏,使用$on 替換模板中的繫結
Vue官網講解避免記憶體洩露https://cn.vuejs.org/v2/cookbook/avoiding-memory-leaks.html
另外,vue 在IE edge瀏覽器下,父子元件的場景,子元件依賴父元件的狀態,子元件控制父元件狀態變化從而反饋給子元件的展示變化,子元件通過v-if模式存在於檢視中,父元件通過狀態控制子元件的v-if狀態變換。子元件控制父元件狀態完成子元件資料填充後,父元件切換子元件的v-if狀態,子元件佔用dom結構被清理。此時,子元件存在時的記憶體佔用未被釋放,當父元件再次回切v-if狀態時,子元件重新展示,記憶體飆升,重複幾次切換後,記憶體飆升明顯,頁面卡頓。
js通常記憶體洩漏的幾種情況的介紹
1.閉包
function fn1(){ var n=1; } //我想取到裡面的區域性變數n function fn1(){ var n=1; function fn2(){//在加一個fn2當他的子集 alert(n); } }
但是我在外面還是訪問不到那就return出來
function fn1(){ var n=1; function fn2(){//在加一個fn2當他的子集 alert(n); } return fn2(); //return出來後 他就給 window了所以一直存在記憶體中。因為一直在記憶體中,在IE裡容易造成記憶體洩漏 } fn1();
儘量書寫的時候,避免這種情況。
2.意外的全域性變數
一個未宣告變數的引用會在全域性物件中建立一個新的變數。在瀏覽器的環境下,全域性物件就是 window,也就是說:
function foo(arg) { bar = "aaaaa"; } 實際上等價於 function foo(arg) { window.bar = "aaaaa"; }
function foo() { this.variable = "qqqqq"; } //this 指向全域性物件(window) foo();
為了防止這種錯誤的發生,可以在你的 JavaScript 檔案開頭新增'use strict';
語句
3.定時器setTimeout setInterval
當不需要setInterval或者setTimeout時,定時器沒有被clear,定時器的回撥函式以及內部依賴的變數都不能被回收,造成記憶體洩漏。比如:vue使用了定時器,需要在beforeDestroy 中做對應銷燬處理。js也是一樣的。
clearTimeout(***) clearInterval(***)
4.如果在mounted/created 鉤子中使用了$on,需要在beforeDestroy 中做對應解綁($off)處理
beforeDestroy() { this.bus.$off('****'); }
5、給DOM物件新增的屬性是一個物件的引用
var testObject = {}; document.getElementById('idname').property = testObject; //如果DOM不被消除,則testObject會一直存在,造成記憶體洩漏
解決方法:
在window.onunload事件中寫上:
window.onunload=function(){ document.getElementById('idname').property = null; //釋放記憶體 };
6.DOM物件與JS物件相互引用
function testObject(element) { this.elementReference = element; // 為testObject(js)物件的屬性繫結element(DOM)物件 element.property = this; // 為element(DOM)物件的屬性繫結testObject(js)物件 } new testObject(document.getElementById('idname'));
解決方法:
在window.onunload事件中寫上:
document.getElementById('idname').property = null;
7.從外到內執行appendChild。這時即使呼叫removeChild也無法釋放
var parentDiv = document.createElement("div"); var childDiv = document.createElement("div"); document.body.appendChild(parentDiv); parentDiv.appendChild(childDiv);
解決方法:
從內到外執行appendChild:
var parentDiv = document.createElement("div"); var childDiv = document.createElement("div"); parentDiv.appendChild(childDiv); document.body.appendChild(parentDiv);
8.反覆重寫同一個屬性會造成記憶體大量佔用(但關閉IE後記憶體會被釋放)
for(i = 0; i < 5000; i++) { hostElement.text = "asdfasdfasdf"; }
這種方式相當於定義了5000個屬性!
9.注意程式邏輯,避免“死迴圈”之類的
10.echarts配合迴圈計時器等出現的記憶體洩漏
感興趣的朋友可以使用線上HTML/CSS/JavaScript程式碼執行工具:http://tools.jb51.net/code/HtmlJsRun測試上述程式碼執行效果。
更多關於JavaScript相關內容可檢視本站專題:《JavaScript操作DOM技巧總結》、《JavaScript頁面元素操作技巧總結》、《JavaScript事件相關操作與技巧大全》、《JavaScript查詢演算法技巧總結》、《JavaScript資料結構與演算法技巧總結》、《JavaScript遍歷演算法與技巧總結》及《JavaScript錯誤與除錯技巧總結》
希望本文所述對大家JavaScript程式設計有所幫助。