1. 程式人生 > 程式設計 >JS造成記憶體洩漏的幾種情況例項分析

JS造成記憶體洩漏的幾種情況例項分析

本文例項講述了JS造成記憶體洩漏的幾種情況。分享給大家供大家參考,具體如下:

介紹:

js中的記憶體垃圾回收機制:垃圾回收器會定期掃描記憶體,當某個記憶體中的值被引用為零時就會將其回收。當前變數已經使用完畢但依然被引用,導致垃圾回收器無法回收這就造成了記憶體洩漏。傳統頁面每次跳轉都會釋放記憶體,所以並不是特別明顯。

Vue單頁面應用中:Web App 與 傳統Web的區別,因為Web App是單頁面應用頁面通過路由跳轉不會重新整理頁面,導致記憶體洩漏不斷堆積,導致頁面卡頓。

洩漏點:

1.DOM/BOM 物件洩漏
2.script 中存在對DOM/BOM 物件的引用導致
3.Javascript 物件洩漏

4.通常由閉包導致,比如事件處理回撥,導致DOM物件和指令碼中物件雙向引用,這個時常見的洩漏原因

程式碼關注點:

1.DOM中的addEventLisner 函式及派生的事件監聽, 比如Jquery 中的on 函式, vue 元件例項的 $on 函式,第三方庫中的初始化函式
2.其它BOM物件的事件監聽, 比如websocket 例項的on 函式
3.避免不必要的函式引用
4.如果使用render 函式,避免在html標籤中繫結DOM/BOM 事件

Vue如何處理:

1.如果在mounted/created 鉤子中綁定了DOM/BOM 物件中的事件,需要在beforeDestroy 中做對應解綁處理

2.如果在mounted/created 鉤子中使用了第三方庫初始化,需要在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程式設計有所幫助。