JS有完善的內存處理機制,因此以前咱們不用特別的去關注這塊的實現。頁面不快了,刷新一下就行了;瀏覽器卡頓,重啓一下就OK。可是隨着SPA和移動APP的流行,以及將來可能存在的PWA的實現,JS內存可能成爲新的內存瓶頸。這也是寫本文的初衷。
當咱們決定再也不使用某些內存時,因爲錯誤的編碼,未能使得GC(Gabbage Collection)正確的將這些內存回收的狀況,就是內存泄漏。javascript
一個對象佔用的內存分爲直接佔用內存(Shallow Size)和佔用總內存(Retained Size)。html
直接佔用內存:對象自己佔用的內存。典型的JavaScript對象都會有保留內存用來描述這個對象和存儲它的直接值。通常,只有數組和字符串會有明顯的直接佔用內存(Shallow Size)。但字符串和數組經常會在渲染器內存中存儲主要數據部分,僅僅在JavaScript對象棧中暴露一個很小的包裝對象。
佔用總內存:直接佔用內存和這個引用的依賴對象所佔用的內存。
賦值和New操做都會涉及到內存的佔用。java
Chrome V8的垃圾回收(GC)算法基於Generational Collection,內存被劃分爲兩種,分別稱爲Young Generation(YG)和Old Generation(OG)。node
所謂Young和Old是根據他們佔用的時間來劃分的。內存在YG的分配和回收快而頻繁,通常存在的時間很短,因此稱爲Young;而在OG中則慢而少發生,因此稱爲Old。
由於在V8中,YG的GC過程會阻塞程序,而OG的GC不會阻塞。因此一般狀況下開發者更關心YG的細節。算法
YG又被平分爲兩部分空間,分別稱爲From和To。全部內存從To空間被分配出去,當To滿時,開始觸發GC,接下來細看一下。chrome
某時刻,To已經分A、B和C分配了內存,當前它剩下一小塊內存未分配出去,而From全部的內存都空閒着。數組
此時,一個程序須要爲D分配內存,但D須要的內存大小超出了To未分配的內存,以下圖。此時,觸發GC,頁面中止執行。
瀏覽器
接着From和To進行對換,即原來的To空間被標誌爲From,From被標誌爲To。而且把活的變量值(例如B)標誌出來,而」垃圾「(例如AC)未被標誌,它們將會被清掉。
閉包
活的B會被複制到To空間,而「垃圾」AC則被回收,同時,D被分配到To空間,最後成下圖的分佈dom
至此,整個GC完成,此過程當中頁面中止執行,因此要儘量的快。當YG中的值存活比較久時,它會被推向OG,OG的空間滿時,觸發OG內的GC,OG的GC時會觸發YG的GC。
- 每次分配都使To的可用空間減少,程序又更接近GC
- YG的GC會阻塞程序,因此GC時間不宜太長10ms之內,由於16ms就會出現丟幀;GC不宜太頻繁
- 某個值變成垃圾後,不會立馬釋放內存,只有在GC的時候所佔內存纔會被回收。
2.2 內容均來自參考文獻
GC Root是內存的根結節,在瀏覽器中它是window,在NodeJS中則是global對象。
從GC Root開始遍歷圖,全部能到達的節點稱爲活節點,若是存在GC Root不能到達的節點,那麼該節點稱爲「垃圾」,將會被回收,如圖中灰色的節點。
至於根節點的回收,不受用戶的控制。
由於沒有徹底切斷與根節點之間的路徑,致使自動GC不會回收這部份內存,從而形成內存泄漏。
具體的緣由有:
var a, b; a.reference = b; b.reference = a;
a = "1234567"; 至關於 window.a = "1234567";
<div id="myDiv"> <input type="button" value="Click me" id="myBtn"> </div> <script type="text/javascript"> var btn = document.getElementById('myBtn'); btn.onclick = function () { document.getElementById('myDiv').innerHTML = 'Processing...'; /* 清除事件綁定 */ // btn.onclick = null; }; </script>
function bindEvent() { var obj = document.getElementById('xxx'); obj.onclick = function () { /** 空函數*/ }; /** delete this reference */ // obj = null; }
// b是a的子dom節點, a是body的子節點 var aElement = document.getElementById("a"); var bElement = document.getElementById("b"); document.body.removeChild(aElement); // aElement = null; // bElement = null;
更多的出如今nodejs中,例如:
while(1) { // do sth }
var arr = []; for (var i=0; i< 100000000000; i++) { var a = { 'desc': 'an object' } arr.push(a); }
本文描述了內存分配和泄漏的基本原理,並說起了平常常遇到的集中的泄漏緣由。在下一篇文章中,將闡述如何肯定內存泄漏,以及可使用的工具和方法。