http://www.ruanyifeng.com/blog/2017/04/memory-leak.html
什么是內存泄漏?
內存泄漏是指不再用到的內存,沒有及時釋放。既不能使用,又不能回收。
程序的運行需要內存。對於持續運行的進程,如果不及時釋放不再用到的內存,內存占用越來越高,輕則影響系統性能,重則導致進程崩潰。
要了解 JS 內存泄漏的幾種情況,我們首先來了解一下 JS 的內存是如何管理的,即 JS 的垃圾收集機制。
垃圾收集機制
Javascript 具有自動垃圾收集機制,也就是說,執行環境會負責管理代碼執行過程中的使用的內存。所需內存的分配 以及無用的回收 完全實現了自動管理。
JavaScript 垃圾回收機制很簡單:找出不再使用的變量,然后釋放掉其占用的內存。但是這個過程不是時時的,因為其開銷比較大,所以垃圾回收器會按照固定的時間間隔周期性的執行。
JavaScript 中最常用的垃圾收集方式有 2 種:標記清除和引用計數。
1)“標記清除”——當變量進入環境(例如,在函數中聲明一個變量)時,就將這個變量標記為“進入環境”。當變量離開環境時,這將其 標記為“離開環境”。
從邏輯上講,永遠不能釋放進入環境的變量所占的內存,因為只要執行流進入相應的環境,就可能用到它們。
function fun(){
var a = 1;//被標記 進入環境
var b= 2;//被標記 進入環境
}
fun();//執行完畢之后,a,b 被標記離開環境,被回收
如果我們的代碼寫法不當,會讓變量一直處於“進入環境”的狀態,無法被回收。
2)“引用計數”——語言引擎有一張”引用表”,跟蹤記錄每個值被引用的次數。
如果一個值的引用次數是0,就表示這個值不再用到了,因此可以將這塊內存釋放。
如果一個值不再需要了,引用數卻不為0,垃圾回收機制無法釋放這塊內存,從而導致內存泄漏。
那些很占空間的值,一旦不再用到,你必須檢查是否還存在對它們的引用。如果是的話,就必須手動解除引用。
function test(){
var a = {} ; //a的引用次數為0
var b = a ; //a的引用次數加1,為1
var c = a; //a的引用次數再加1,為2
var b ={}; //a的引用次數減1,為1
}
導致內存泄漏的幾種情況
1. 意外的全局變量
function leaks(){
leak = 'xxxxxx';//leak 成為一個全局變量,不會被回收
}
調用完函數以后,變量仍然存在,導致泄漏.
你可以通過加上 ‘use strict’ 啟用嚴格模式來避免這類問題, 嚴格模式會阻止你創建意外的全局變量.
2. 閉包
閉包可以維持函數內局部變量,使其得不到釋放。
解決辦法:在函數外部定義事件處理函數,解除閉包。或在閉包中,刪除沒用的屬性以減少對內存的消耗。或在外部函數中刪除對DOM的引用
function bindEvent()
{
var obj=document.createElement("XXX");
obj.onclick=function(){
//Even if it's a empty function
}
obj=null;
}
3. 未清除 dom 元素的引用
dom 元素移除,但 對 dom 元素的引用沒有解除,會導致內存泄漏。
解決辦法:手工移除。
var elements = {
button: document.getElementById('button'),
image: document.getElementById('image')
}
function doStuff() {
image.src = 'http://some.url/image';
button.click();
console.log(text.innerHTML);
}
function removeButton() {
document.body.removeChild(document.getElementById('button'));
// 雖然我們用removeChild移除了button, 但是還在elements對象里保存着#button的引用
// 換言之, DOM元素還在內存里面.
}
4.循環引用
循環引用 在引用計數策略下會導致內存泄漏,標記清除不會。
解決辦法:手工解除循環引用。
function fn() {
var a = {};
var b = {};
a.pro = b;
b.pro = a;
}
fn();
a和b的引用次數都是2,fn()執行完畢后,兩個對象都已經離開環境。
在標記清除方式下是沒有問題的,但是在引用計數策略下,a和b的引用次數不為0,不會被垃圾回收器回收內存。如果fn函數被大量調用,就會造成內存泄漏。
IE中的BOM和DOM中的對象使用C++以COM(component Object Model,組件對象模型)對象的形式實現而COM對象的垃圾收集機制采用的是引用計數策略。換句話說,只要在IE中涉及COM對象,就會存在循環引用的問題。
var element = document.getElementById("some_element");
var myObject = new Object();
myObject.e = element;
element.o = myObject;
//手工斷開它們之間的連接
myObject.element=null;
element.someObject=null;
5. 被遺忘的計時器或回調
var someResource = getData();
setInterval(function() {
var node = document.getElementById('Node');
if(node) {
node.innerHTML = JSON.stringify(someResource));
}
}, 1000);
原文:https://blog.csdn.net/meijory/article/details/76839072
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!