溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

JavaScript內存泄漏的主要原因

發布時間:2020-11-10 10:24:23 來源:億速云 閱讀:262 作者:小新 欄目:web開發

這篇文章主要介紹JavaScript內存泄漏的主要原因,文中介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們一定要看完!


前言

在閱讀這篇博客之前,你或許需要具備一些JavaScript內存管理的知識:

  • V8中JavaScript的內存管理與垃圾回收

1 介紹

內存泄露(Memory Leaks):是指應用程序已經不再需要的內存,由于某種原因未返回給操作系統或者空閑內存池(Pool of Free Memory)。

內存泄露可能帶來的問題:變慢、卡頓、高延遲。

2 內存泄露的主要原因

JavaScript內存泄漏的主要原因在于一些不再需要的引用(Unwanted References)。

所謂的Unwanted References指的是:有一些內存,其實開發人員已經不再需要了,但是由于某種原因,這些內存仍然被標記并保留在活動根目錄樹中。Unwanted References就是指對這些內存的引用。在JavaScript上下文中,Unwanted References是一些不再使用的變量,這些變量指向了原本可以釋放的一些內存。

3 常見的內存泄露

3.1 全局變量

首先,我們得知道,JavaScript中的全局變量是由根節點(root node)引用的,因此它們在應用程序的整個生命周期中都不會被垃圾回收。

場景一:在JavaScript中,如果引用未聲明的變量,將會導致,在全局環境中創建新的變量。

function foo(arg) {    
bar = "this is a hidden global variable";
}

上面這串代碼,實際上如下:

function foo(arg) {   
window.bar = "this is an explicit global variable";
}

假如,我們希望bar這個變量僅在foo函數作用域內部使用,但上面這種情況就會意外地在全局作用域內創建bar,這將造成內存泄漏。

場景二:

function foo() {    
this.variable = "potential accidental global";
}foo();

同樣的,如果我們希望bar這個變量僅在foo函數作用域內部使用,但如果不知道foo函數內部的this指向全局對象,將造成內存泄露。

建議:

  1. 避免意外地創建全局變量。比如,我們可以使用嚴格模式,則本節的第一段代碼將報錯,而不會創建全局變量。

  2. 減少創建全局變量。

  3. 如果必須使用全局變量來存儲大量數據,請確保在處理完數據后將其置null或重新分配。

3.2 計時器

場景舉例:

for (var i = 0; i < 100000; i++) 
{    
var buggyObject = {        
callAgain: function () {            
var ref = this;            
var val = setTimeout(function () 
{                
ref.callAgain();            
}, 10);        
}    
}    
buggyObject.callAgain();    
buggyObject = null;}

3.3 多處引用

多處引用(Multiple references):當多個對象均引用同一對象時,但凡其中一個引用沒有清除,都將導致被引用對象無法GC。

場景一:

var elements = 
{    
button: document.getElementById('button'),    
image: document.getElementById('image'),    
text: document.getElementById('text')};function doStuff() 
{    
image.src = 'http://some.url/image';    
button.click();    
console.log(text.innerHTML);    
// Much more logic}function removeButton() 
{    // The button is a direct child of body.    
document.body.removeChild(document.getElementById('button'));    
// At this point, we still have a reference to #button in the global    
// elements dictionary. In other words, the button element is still in    
// memory and cannot be collected by the GC.s}

在上面這種情況中,我們對#button的保持兩個引用:一個在DOM樹中,另一個在elements對象中。 如果將來決定回收#button,則需要使兩個引用均不可訪問。在上面的代碼中,由于我們只清除了來自DOM樹的引用,所以#button仍然存在內存中,而不會被GC。

場景二: 如果我們想要回收某個table,但我們保持著對這個table中某個單元格(cell)的引用,這個時候將導致整個table都保存在內存中,無法GC。

3.4 閉包

閉包(Closure):閉包是一個函數,它可以訪問那些定義在它的包圍作用域(Enclosing Scope)里的變量,即使這個包圍作用域已經結束。因此,閉包具有記憶周圍環境(Context)的功能。

場景舉例:

var newElem;function outer() 
{   
var someText = new Array(1000000);   
var elem = newElem;   
function inner() 
{       if (elem) return someText;  
 }   
 return function () {};
 }setInterval(function ()
  {   newElem = outer();}, 5);

在這個例子中,有兩個閉包:一個是inner,另一個是匿名函數function () {}。其中,inner閉包引用了someText和elem,并且,inner永遠也不會被調用??墒?,我們需要注意:相同父作用域的閉包,他們能夠共享context。 也就是說,在這個例子中,inner的someText和elem將和匿名函數function () {}共享。然而,這個匿名函數之后會被return返回,并且賦值給newElem。只要newElem還引用著這個匿名函數,那么,someText和elem就不會被GC。

同時,我們還要注意到,outer函數內部執行了var elem = newElem;,而這個newElem引用了上一次調用的outer返回的匿名函數。試想,第n次調用outer將保持著第n-1次調用的outer中的匿名函數,而這個匿名函數由保持著對elem的引用,進而保持著對n-2次的...因此,這將造成內存泄漏。

解決方案:setInterval中的參數1的代碼改為newElem = outer()();

這一節內容的具體剖析,可以見資料1和資料2。

4 Chrome內存分析工具

Chrome(最新的86版本)開發者工具中有兩個關于內存的分析工具:

  1. PerformanceJavaScript內存泄漏的主要原因

  2. Memory

以上是JavaScript內存泄漏的主要原因的所有內容,感謝各位的閱讀!希望分享的內容對大家有幫助,更多相關知識,歡迎關注億速云行業資訊頻道!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

亚洲午夜精品一区二区_中文无码日韩欧免_久久香蕉精品视频_欧美主播一区二区三区美女