JS有完善的記憶體處理機制,所以之前我們不用特別的去關注這塊的實作。頁面不快了,刷新一下就好了;瀏覽器卡頓,重啟一下就OK。但是隨著SPA和行動APP的流行,以及未來可能存在的PWA的實現,JS記憶體可能成為新的記憶體瓶頸。
當我們決定不再使用某些記憶體時,由於錯誤的編碼,未能使得GC (Gabbage Collection)正確的將這些記憶體回收的情況,就是記憶體洩漏。
一個物件佔用的記憶體分為直接佔用記憶體(Shallow Size)和占用總記憶體(Retained Size)。
直接佔用記憶體:物件本身所佔用的記憶體。典型的JavaScript物件都會有保留記憶體用來描述這個物件和儲存它的直接值。一般,只有陣列和字串會有明顯的直接佔用記憶體(Shallow Size)。但字串和陣列常常會在渲染器記憶體中儲存主要資料部分,僅在JavaScript物件堆疊中暴露一個很小的包裝物件。
佔用總記憶體:直接佔用記憶體和這個引用的依賴物件所佔用的記憶體。
賦值和New運算都會牽涉到記憶體的佔用。
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 內容皆來自參考文獻
GC Root是記憶體的根結節,在瀏覽器中它是window,在NodeJS中則是global物件。
從GC Root開始遍歷圖,所有能到達的節點稱為活節點,如果存在GC Root無法到達的節點,那麼該節點稱為“垃圾”,將會被回收,如圖中灰色的節點。
至於根節點的回收,不受使用者的控制。
因為沒有完全切斷與根節點之間的路徑,導致自動GC不會回收這部分內存,從而造成內存洩漏。
具體的原因有:
#物件之間的相互引用
##
<span style="font-size: 14px;">var a, b;<br>a.reference = b;<br>b.reference = a;<br></span>
#
<span style="font-size: 14px;">a = "1234567";<br>相当于<br>window.a = "1234567";<br></span>
<span style="font-size: 14px;"><p id="myp"><br> <input type="button" value="Click me" id="myBtn"><br></p><br><br><script type="text/javascript"><br> var btn = document.getElementById('myBtn');<br> btn.onclick = function () {<br> document.getElementById('myp').innerHTML = 'Processing...';<br> /* 清除事件绑定 */<br> // btn.onclick = null;<br> };<br></script><br></span>
#閉包引用
<span style="font-size: 14px;">function bindEvent() {<br> var obj = document.getElementById('xxx');<br><br> obj.onclick = function () {<br> /** 空函数*/<br> };<br><br> /** delete this reference */<br> // obj = null;<br>}<br></span>
DOM元素清空或刪除時,子元素存在JS引用,導致子元素的所有父元素都不會被刪除
<span style="font-size: 14px;">// b是a的子dom节点, a是body的子节点<br>var aElement = document.getElementById("a");<br>var bElement = document.getElementById("b");<br>document.body.removeChild(aElement);<br>// aElement = null;<br>// bElement = null;<br></span>
3.2 過度佔用了記憶體空間
<span style="font-size: 14px;">while(1) {<br> // do sth<br>}<br></span>
<span style="font-size: 14px;">var arr = [];<br>for (var i=0; i< 100000000000; i++) {<br> var a = {<br> 'desc': 'an object'<br> }<br> arr.push(a);<br>}<br></span>
以上是JS記憶體管理實例講解的詳細內容。更多資訊請關注PHP中文網其他相關文章!