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

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. 意外的全局變量引起的內存泄漏。

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

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

  1. 閉包引起的內存泄漏

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

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

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

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

解決:手動刪除。

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

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

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

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

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

解決:手動刪除清空。

內存泄露解決舉例:

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

實例如下:

複製代碼

複製代碼 解決方法如下:

複製代碼

複製代碼 (2)、由於是函數內定義函數,並且內部函數--事件回調的引用外暴了,形成了閉包。閉包可以維持函數內局部變量,使其得不到釋放。 實例如下: function bindEvent(){ var obj=document.createElement("XXX"); obj.οnclick=function(){ //Even if it's a empty function } } 解決方法如下:

複製代碼
function bindEvent(){
var obj=document.createElement(“XXX”);
obj.οnclick=function(){
//Even if it’s a empty function
}
obj=null;
}
複製代碼

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章