前端之淺談瀏覽器的垃圾回收機制和內存泄露


JavaScript使用垃圾回收機制來自動管理內存。

JS的回收機制分兩種:1.標記清除 2.引用計數。各大瀏覽器常用的是前者。

比如,Chrome瀏覽器限制的所能使用的內存極限(64位為1.4GB,32位為1.0GB),這就意味着瀏覽器將無法直接操作一些大內存對象。

標記清除:

  定義和用法:當變量進入環境時,將變量標記"進入環境",當變量離開環境時,標記為:"離開環境"。某一個時刻,垃圾回收器會過濾掉環境中的變量,以及被環境變量引用的變量,剩下的就是被視為准備回收的變量。

  到目前為止,IE、Firefox、Opera、Chrome、Safari的js實現使用的都是標記清除的垃圾回收策略或類似的策略,只不過垃圾收集的時間間隔互不相同。

      工作流程:

1.    垃圾回收器,在運行的時候會給存儲在內存中的所有變量都加上標記。

2.    去掉環境中的變量以及被環境中的變量引用的變量的標記。

3.    再被加上標記的會被視為准備刪除的變量。

4.    垃圾回收器完成內存清除工作,銷毀那些帶標記的值並回收他們所占用的內存空間。

 

引用計數:

  定義和用法:引用計數是跟蹤記錄每個值被引用的次數。

  基本原理:就是變量的引用次數,被引用一次則加1,當這個引用計數為0時,被視為准備回收的對象。

       工作流程:

1.    聲明了一個變量並將一個引用類型的值賦值給這個變量,這個引用類型值的引用次數就是1。

2.    同一個值又被賦值給另一個變量,這個引用類型值的引用次數加1.

3.    當包含這個引用類型值的變量又被賦值成另一個值了,那么這個引用類型值的引用次數減1.

4.    當引用次數變成0時,說明沒辦法訪問這個值了。

5.    當垃圾收集器下一次運行時,它就會釋放引用次數是0的值所占的內存。

但是循環引用的時候就會釋放不掉內存。循環引用就是對象A中包含另一個指向對象B的指針,B中也包含一個指向A的引用。

因為IE中的BOM、DOM的實現使用了COM,而COM對象使用的垃圾收集機制是引用計數策略。所以會存在循環引用的問題。

解決:手工斷開js對象和DOM之間的鏈接。賦值為null。IE9把DOM和BOM轉換成真正的JS對象了,所以避免了這個問題。

 

 內存管理

1、什么時候觸發垃圾回收?

垃圾回收器周期性運行,如果分配的內存非常多,那么回收工作也會很艱巨,確定垃圾回收時間間隔就變成了一個值得思考的問題。

IE6的垃圾回收是根據內存分配量運行的,當環境中的變量,對象,字符串達到一定數量時觸發垃圾回收。垃圾回收器一直處於工作狀態,嚴重影響瀏覽器性能。

IE7中,垃圾回收器會根據內存分配量與程序占用內存的比例進行動態調整,開始回收工作。

2、合理的GC方案:(1)、遍歷所有可訪問的對象; (2)、回收已不可訪問的對象。

3、GC缺陷:(1)、停止響應其他操作;

4、GC優化策略:(1)、分代回收(Generation GC);(2)、增量GC

開發過程中遇到的內存泄露情況

1、定義和用法:

內存泄露是指一塊被分配的內存既不能使用,又不能回收,直到瀏覽器進程結束。C#和Java等語言采用了自動垃圾回收方法管理內存,幾乎不會發生內存泄露。我們知道,瀏覽器中也是采用自動垃圾回收方法管理內存,但由於瀏覽器垃圾回收方法有bug,會產生內存泄露。

 

由於每次的垃圾回收開銷都相對較大,並且由於機制的一些不完善的地方,可能會導致內存泄露。我們可以利用一些方法減少垃圾回收,並且盡量避免循環引用的問題。

 

例如,在對象結束使用后 ,令obj = null。這樣利於解除循環引用,使得無用變量及時被回收。

 

再如,js中開辟空間的操作有new(), [ ], { }, function (){..}。在創建新對象的時候要盡量考慮增大對象的復用性。

 

2、內存泄露的幾種情況:

雖然有垃圾回收機制,但是,我們編寫代碼操作不當還是會造成內存泄漏。

1.    意外的全局變量引起的內存泄漏。

原因:全局變量,不會被回收。

解決:使用嚴格模式避免。

2.    閉包引起的內存泄漏

原因:閉包可以維持函數內局部變量,使其得不到釋放。

解決:將事件處理函數定義在外部,解除閉包,或者在定義事件處理函數的外部函數中,刪除對dom的引用。

3.    沒有清理的DOM元素引用

原因:雖然別的地方刪除了,但是對象中還存在對dom的引用

解決:手動刪除。

4.    被遺忘的定時器或者回調

原因:定時器中有dom的引用,即使dom刪除了,但是定時器還在,所以內存中還是有這個dom。

解決:手動刪除定時器和dom。

5.    子元素存在引用引起的內存泄漏

原因:div中的ul li  得到這個div,會間接引用某個得到的li,那么此時因為div間接引用li,即使li被清空,也還是在內存中,並且只要li不被刪除,他的父元素都不會被刪除。

解決:手動刪除清空。

 

內存泄露解決舉例:

(1)、當頁面中元素被移除或替換時,若元素綁定的事件仍沒被移除,在IE中不會作出恰當處理,此時要先手工移除事件,不然會存在內存泄露。

實例如下:

<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...";
    }
</script>

解決方法如下:

<div id="myDiv">
    <input type="button" value="Click me" id="myBtn">
</div>
<script type="text/javascript">
    var btn = document.getElementById("myBtn");
    btn.onclick = function(){
    btn.onclick = null;
        document.getElementById("myDiv").innerHTML = "Processing...";
    }
</script>
(2)、由於是函數內定義函數,並且內部函數--事件回調的引用外暴了,形成了閉包。閉包可以維持函數內局部變量,使其得不到釋放。
實例如下:
function bindEvent(){
    var obj=document.createElement("XXX");
    obj.onclick=function(){
        //Even if it's a empty function
    }
}

解決方法如下:

function bindEvent(){
    var obj=document.createElement("XXX");
    obj.onclick=function(){
         //Even if it's a empty function
    }
    obj=null;
}

 

參考博文:

1.【面試題——js垃圾回收機制和引起內存泄漏的操作】https://blog.csdn.net/yingzizizizizizzz/article/details/77333996      

2.【2018最新Web前端經典面試試題及答案】https://blog.csdn.net/wdlhao/article/details/79079660

3.【淺談Chrome V8引擎中的垃圾回收機制】https://www.cnblogs.com/liangdaye/p/4654734.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM