1.什么是內存泄漏
當我們決定不再使用某些內存時,由于錯誤的編碼,未能使得GC(Gabbage Collection)正確的將這些內存回收的情況,就是內存泄漏。
2.內存的占用,分配和回收
2.1 內存的占用
一個對象占用的內存分為直接占用內存(Shallow Size)和占用總內存(Retained Size)。
直接占用內存:對象本身占用的內存。典型的Javascript對象都會有保留內存用來描述這個對象和存儲它的直接值。一般,只有數組和字符串會有明顯的直接占用內存(Shallow Size)。但字符串和數組常常會在渲染器內存中存儲主要數據部分,僅僅在Javascript對象棧中暴露一個很小的包裝對象。
占用總內存:直接占用內存和這個引用的依賴對象所占用的內存。
賦值和New操作都會涉及到內存的占用。
2.2 內存的分配
Chrome V8的垃圾回收(GC)算法基于Generational Collection,內存被劃分為兩種,分別稱為Young Generation(YG)和Old Generation(OG)。
所謂Young和Old是根據他們占用的時間來劃分的。內存在YG的分配和回收快而頻繁,一般存在的時間很短,所以稱為Young;而在OG中則慢而少發生,所以稱為Old。
因為在V8中,YG的GC過程會阻塞程序,而OG的GC不會阻塞。所以通常情況下開發者更關心YG的細節。
YG又被平分為兩部分空間,分別稱為From和To。所有內存從To空間被分配出去,當To滿時,開始觸發GC,接下來細看一下。
某時刻,To已經分A、B和C分配了內存,當前它剩下一小塊內存未分配出去,而From所有的內存都空閑著。
此時,一個程序需要為D分配內存,但D需要的內存大小超出了To未分配的內存,如下圖。此時,觸發GC,頁面停止執行。
接著From和To進行對換,即原來的To空間被標志為From,From被標志為To。并且把活的變量值(例如B)標志出來,而”垃圾“(例如AC)未被標志,它們將會被清掉。
活的B會被復制到To空間,而「垃圾」AC則被回收,同時,D被分配到To空間,最后成下圖的分布
至此,整個GC完成,此過程中頁面停止執行,所以要盡可能的快。當YG中的值存活比較久時,它會被推向OG,OG的空間滿時,觸發OG內的GC,OG的GC時會觸發YG的GC。
每次分配都使To的可用空間減小,程序又更接近GC
YG的GC會阻塞程序,所以GC時間不宜太長10ms以內,因為16ms就會出現丟幀;GC不宜太頻繁
某個值變成垃圾后,不會立馬釋放內存,只有在GC的時候所占內存才會被回收。
2.2 內容均來自參考文獻
2.3 內存的回收
GC Root是內存的根結節,在瀏覽器中它是window,在NodeJS中則是global對象。
從GC Root開始遍歷圖,所有能到達的節點稱為活節點,如果存在GC Root不能到達的節點,那么該節點稱為“垃圾”,將會被回收,如圖中灰色的節點。
至于根節點的回收,不受用戶的控制。
3. 導致內存泄漏的原因
3.1 沒有完全切斷與GC root之間的路徑
因為沒有完全切斷與根節點之間的路徑,導致自動GC不會回收這部分內存,從而造成內存泄漏。
具體的原因有:
對象之間的相互引用
var a, b;
a.reference = b;
b.reference = a;
錯誤使用了全局變量
a = "1234567";
相當于
window.a = "1234567";
DOM元素清空或刪除時,綁定的事件未清除
<p id="myp">
<input type="button" value="Click me" id="myBtn">
</p>
<script type="text/javascript">
var btn = document.getElementById('myBtn');
btn.onclick = function () {
document.getElementById('myp').innerHTML = 'Processing...';
// btn.onclick = null;
};
</script>
閉包引用
function bindEvent() {
var obj = document.getElementById('xxx');
obj.onclick = function () {
};
// obj = null;
}
DOM元素清空或刪除時,子元素存在JS引用,導致子元素的所有父元素都不會被刪除
// b是a的子dom節點, a是body的子節點
var aElement = document.getElementById("a");
var bElement = document.getElementById("b");
document.body.removeChild(aElement);
// aElement = null;
// bElement = null;
3.2 過度占用了內存空間
更多的出現在nodejs中,例如:
無節制的循環
while(1) {
// do sth
}
過大的數組
var arr = [];
for (var i=0; i< 100000000000; i++) {
var a = {
'desc': 'an object'
}
arr.push(a);
}
相關推薦:
詳細介紹Linux中的內存管理
php內存管理之垃圾回收機制的詳解(圖)
如何避免Javascript的內存泄露及內存管理技巧
以上就是JS內存管理實例講解的詳細內容,更多請關注php中文網其它相關文章!