1. 程式人生 > >JavaScript 垃圾回收

JavaScript 垃圾回收

在公司經常會聽到大牛們討論時說道記憶體洩露神馬的,每每都驚羨不已,最近精力主要用在了Web 開發上,讀了一下《JavaScript高階程式設計》(書名很唬人,實際作者寫的特別好,由淺入深)瞭解了一下JavaScript垃圾回收機制,對記憶體洩露有了一定的認識。

和C#、Java一樣JavaScript有自動垃圾回收機制,也就是說執行環境會負責管理程式碼執行過程中使用的記憶體,在開發過程中就無需考慮記憶體分配及無用記憶體的回收問題了。JavaScript垃圾回收的機制很簡單:找出不再使用的變數,然後釋放掉其佔用的記憶體,但是這個過程不是時時的,因為其開銷比較大,所以垃圾回收器會按照固定的時間間隔週期性的執行。

變數生命週期

有同學看了上面就會問了,什麼叫不再使用的變數?不再使用的變數也就是生命週期結束的變數,當然只可能是區域性變數,全域性變數的生命週期直至瀏覽器解除安裝頁面才會結束。區域性變數只在函式的執行過程中存在,而在這個過程中會為區域性變數在棧或堆上分配相應的空間,以儲存它們的值,然後再函式中使用這些變數,直至函式結束(閉包中由於內部函式的原因,外部函式並不能算是結束,瞭解閉包可以看看 JavaScript作用域鏈JavaScript 閉包究竟是什麼)。

一旦函式結束,區域性變數就沒有存在必要了,可以釋放它們佔用的記憶體。貓和很簡單的工作,為什麼會有很大開銷呢?這僅僅是垃圾回收的冰山一角,就像剛剛提到的閉包,貌似函式結束了,其實還沒有,垃圾回收器必須那個變數游泳,那個變數沒用,對於不再有用的變數打上標記,以備將來回收。用於標記無用的策略有很多,常見的有兩種方式

標記清除(mark and sweep)

這是JavaScript最常見的垃圾回收方式,當變數進入執行環境的時候,比如函式中宣告一個變數,垃圾回收器將其標記為“進入環境”,當變數離開環境的時候(函式執行結束)將其標記為“離開環境”。至於怎麼標記有很多種方式,比如特殊位的反轉、維護一個列表等,這些並不重要,重要的是使用什麼策略,原則上講不能夠釋放進入環境的變數所佔的記憶體,它們隨時可能會被呼叫的到。

垃圾回收器會在執行的時候給儲存在記憶體中的所有變數加上標記,然後去掉環境中的變數以及被環境中變數所引用的變數(閉包),在這些完成之後仍存在標記的就是要刪除的變量了,因為環境中的變數已經無法訪問到這些變量了,然後垃圾回收器相會這些帶有標記的變數機器所佔空間。

大部分瀏覽器都是使用這種方式進行垃圾回收,區別在於如何標記及垃圾回收間隔而已,只有低版本IE,不出所料,又是IE。。。

引用計數(reference counting)

在低版本IE中經常會出現記憶體洩露,很多時候就是因為其採用引用計數方式進行垃圾回收。引用計數的策略是跟蹤記錄每個值被使用的次數,當聲明瞭一個變數並將一個引用型別賦值給該變數的時候這個值的引用次數就加1,如果該變數的值變成了另外一個,則這個值得引用次數減1,當這個值的引用次數變為0的時候,說明沒有變數在使用,這個值沒法被訪問了,因此可以將其佔用的空間回收,這樣垃圾回收器會在執行的時候清理掉引用次數為0的值佔用的空間。

看起來也不錯的方式,為什麼很少有瀏覽器採用,還會帶來記憶體洩露問題呢?主要是因為這種方式沒辦法解決迴圈引用問題。比如物件A有一個屬性指向物件B,而物件B也有有一個屬性指向物件A,這樣相互引用

function test(){
            var a={};
            var b={};
            a.prop=b;
            b.prop=a;
        }

這樣a和b的引用次數都是2,即使在test()執行完成後,兩個物件都已經離開環境,在標記清除的策略下是沒有問題的,離開環境的就被清除,但是在引用計數策略下不行,因為這兩個物件的引用次數仍然是2,不會變成0,所以其佔用空間不會被清理,如果這個函式被多次呼叫,這樣就會不斷地有空間不會被回收,造成記憶體洩露。

在IE中雖然JavaScript物件通過標記清除的方式進行垃圾回收,但BOM與DOM物件卻是通過引用計數回收垃圾的,也就是說只要涉及BOM及DOM就會出現迴圈引用問題。看上面的例子,有同學回覺得太弱了,誰會做這樣無聊的事情,其實我們是不是就在做

window.onload=function outerFunction(){
        var obj = document.getElementById("element");
        obj.onclick=function innerFunction(){};
    };

這段程式碼看起來沒什麼問題,但是obj引用了document.getElementById("element"),而document.getElementById("element")的onclick方法會引用外部環境中德變數,自然也包括obj,是不是很隱蔽啊。

解決辦法

最簡單的方式就是自己手工解除迴圈引用,比如剛才的函式可以這樣

window.onload=function outerFunction(){
        var obj = document.getElementById("element");
        obj.onclick=function innerFunction(){};
       obj=null;
    };

什麼時候觸發垃圾回收

垃圾回收器週期性執行,如果分配的記憶體非常多,那麼回收工作也會很艱鉅,確定垃圾回收時間間隔就變成了一個值得思考的問題。IE6的垃圾回收是根據記憶體分配量執行的,當環境中存在256個變數、4096個物件、64k的字串任意一種情況的時候就會觸發垃圾回收器工作,看起來很科學,不用按一段時間就呼叫一次,有時候會沒必要,這樣按需呼叫不是很好嗎?但是如果環境中就是有這麼多變數等一直存在,現在指令碼如此複雜,很正常,那麼結果就是垃圾回收器一直在工作,這樣瀏覽器就沒法兒玩兒了。

微軟在IE7中做了調整,觸發條件不再是固定的,而是動態修改的,初始值和IE6相同,如果垃圾回收器回收的記憶體分配量低於程式佔用記憶體的15%,說明大部分記憶體不可被回收,設的垃圾回收觸發條件過於敏感,這時候把臨街條件翻倍,如果回收的記憶體高於85%,說明大部分記憶體早就該清理了,這時候把觸發條件置回。這樣就使垃圾回收工作職能了很多。

同C# 、Java一樣我們可以手工呼叫垃圾回收程式,但是由於其消耗大量資源,而且我們手工呼叫的不會比瀏覽器判斷的準確,所以不推薦手工呼叫垃圾回收。

相關推薦

javascript垃圾回收

銷毀 以及 tro script ron 計數 引用計數 asc javascrip javascript具有自動垃圾回收機制,即GC(Garbage Collection),垃圾回收器會按照固定的時間間隔周期性的執行垃圾回收。 垃圾回收有兩種常見做法: 1. 標記清除(多

javascript垃圾回收機制

javascript是一門具有自動垃圾收集機制的程式語言,開發人員不必關心記憶體分配和回收問題。這種垃圾收集機制的原理就是:找出那些不再繼續使用的變數,然後釋放其佔用的記憶體。在javascript中垃圾回收機制有兩種,標記清除和引用計數,和java類似。 垃圾收集機制的原

JavaScript 垃圾回收

在公司經常會聽到大牛們討論時說道記憶體洩露神馬的,每每都驚羨不已,最近精力主要用在了Web 開發上,讀了一下《JavaScript高階程式設計》(書名很唬人,實際作者寫的特別好,由淺入深)瞭解了一下JavaScript垃圾回收機制,對記憶體洩露有了一定的認識。 和C#、Java一樣JavaScript有自動垃

[轉] javascript中的變量和垃圾回收

tor 們的 問題 只有一個 次數 ie9 內存 通過 保持 [From] http://www.imooc.com/article/4585 基本類型和引用類型 js中的變量雖然不區分類型,但是實際上Ecmascript包含兩種類型,基本類型和引用類型. 基本類型有5

JavaScript垃圾回收機制

垃圾回收器 聲明 過程 賦值 cti 變量 global light 垃圾回收 原文   https://www.jianshu.com/p/4aa1a29781cc 大綱   1、認識垃圾回收機制  2、垃圾回收機制的原理  3、垃圾回收機制的標記策略  4、垃圾回收機制

JavaScript基礎概念之----垃圾回收機制

內存空間 工作 清除 UNC var span javascrip 去掉 似的 分為兩種: 標記清除 引用計數 標記清除 當變量進入環境時,就將這個變量標記為“進入環境”。當變量離開環境時,則將其標記為“離開環境”。 垃圾收集器在運行的時候會給存儲在內存中的所有變量都加

JavaScript 的記憶體洩露和垃圾回收

什麼是記憶體洩露 ? 任何程式語言,在執行時都需要使用到記憶體,比如在一個函式中, var arr = [1, 2, 3, 4, 5]; 這麼一個數組,就需要記憶體。 但是,在使用了這些記憶體之後, 如果後面他們不會再被用到,但是還沒有及時釋放,這就叫做記憶體洩露(memory

JavaScript基礎筆記】資料型別轉換、false值、記憶體圖、垃圾回收和深淺拷貝簡易概念

其他型別轉換成字串 xxx.toString()   // var object = {a:1}; object.toString = [object Object]   //這種方法對null undefined使用會報錯 xxx +

JavaScript中的垃圾回收和記憶體洩漏

摘要: JS記憶體管理。 作者:浪裡行舟 Fundebug經授權轉載,版權歸原作者所有。 前言 程式的執行需要記憶體。只要程式

JavaScript垃圾回收機制與記憶體洩漏

常用的兩種演算法: 引用計數(新版瀏覽器已棄用,棄用原因:會出現迴圈引用的情況,無法進行垃圾回收,導致記憶體洩漏) 標記清除 引用計數法 引用計數,顧名思義一個物件是否有指向它的引用,即看棧中是否有指向要釋放的該塊堆記憶體中的地址,如果沒有,則該塊記憶體是不需要的,可以進行釋放,即垃圾回收 下面引用大佬的一個

JavaScript如何工作:垃圾回收機制 + 常見的4種記憶體洩漏

原文地址: How JavaScript works: memory management + how to handle 4 common memory leaks 本文永久連結: https://didiheng.com/front/2019-04-01.html 有部分的刪減和修改,不過大部分

Java 垃圾回收(GC) 泛讀

其中 中斷 bsp 之前 後臺 轉換 actions 一次 需要 Java 垃圾回收(GC) 泛讀 文章地址:https://segmentfault.com/a/1190000008922319 0. 序言 帶著問題去看待 垃圾回收(GC) 會比較好,一般來說主要的疑

Java垃圾回收算法

分代 清理 java 利用 效果 大小 ava 大量 思想 1.標記-清除算法 概念 標記階段:先通過根節點,標記所有從根節點開始的可達對象,因此,未被標記的對象就是未被引用的垃圾對象; 清除階段:清除所有未被標記的對象。 缺點: 標記和清除的過程效率不高(標記和清除都需要

深入解密.NET(GC垃圾回收

clas 不包含 ace 枚舉 double 技術分享 heap system sin 值類型與引用類型 值類型(Value Type),值類型實例通常分配在線程的堆棧(stack)上,並且不包含任何指向實例數據的指針,因為變量本身就包含了其實例數據 C#的所有值類型均隱式

垃圾回收的概念與算法

還要 無法 次數 回收算法 串行 引用 from 標記壓縮 高效 GC中的垃圾,是指的是在內存中不在不再被使用的對象。 常見的垃圾回收算法 1.引用計數算法(無法回收循環引用的對象) 2.標記清除算法分為標記階段和清除階段(會產生內存的空間碎片) 3.復制算法(缺點是將系統

JVM-垃圾回收

本地變量 整體 垃圾回收算法 系統通知 string類型 發現 font 對象實例 rom 1.垃圾回收如何判定   1.1引用計數法     引用計數法是給對象添加一個引用計數器,當有對該對象的引用時,計數器加1,引用失效時,計數減1,計數器為0時不能再使用.該對象可以被

Python引用復制,參數傳遞,弱引用與垃圾回收

++ 左值 較差 計數 call all 思想 git 引用類型 引用 先上個示例: >>> val = [1] >>> val[0] = val >>> val [[...]] 上述代碼使val中包含自身,而產生了無限

JVM GC算法 垃圾回收

com 修正 可用 mark 信息 網站 最長 style 互聯網 JVM的垃圾回收算法有三種: 1.標記-清除(mark-sweep):啥都不說,直接上圖 2.標記-整理(mark-compact) 3.復制(copy) 分代收集算法

java中存在垃圾回收機制,但是還會有內存泄漏的問題,原因是

java 自己 data .so 這樣的 即使 垃圾 ref stack 答案是肯定的,但不能拿這一句回答面試官的問題。分析:JAVA是支持垃圾回收機制的,在這樣的一個背景下,內存泄露又被稱為“無意識的對象保持”。如果一個對象引用被無意識地保留下來,那麽垃圾回收器不僅不會處

Hotspot垃圾回收

ref collect serial 使用 區域 rec data 是否 而是 Hotspot垃圾回收器 HotSpot虛擬機提供了多種垃圾收集器,每種收集器都有各自的特點,沒有最好的垃圾收集器,只有最適合的垃圾收集器。我們可以根據自己實際的應用需求選擇最適合的垃