原理:找出不使用的變量,釋放內存。
JavaScript是使用垃圾回收的語言,也就是很大的解決了跟蹤內存對開發者造成的負擔(畢竟這是很多問題的來源)。而卸下這個甜蜜的負擔(一點也不甜蜜好嘛),通過自動內存管理實現內存分配和閑置資源回收。(下面會簡單的講述內存泄漏)
如果不用垃圾回收機制會造成什么后果?
1.會消耗掉所有的可用內存(內存占用),造成系統崩潰
2.內存泄漏
可達性:一個地方到另一個地方的容易程度。
GC的最主要的職責是監控數據的可達性
1.所有顯示調用,被稱為根,全局對象
正被調用的函數的局部變量參數
相關嵌套函數里的變量和參數
其他(引擎內部調用的一些變量)
2.所用從根引用或引用鏈訪問的對象
什么時候觸發垃圾回收機制
該過程是周期性的,每隔一個固定的時間,就會自動運行程序。
如果分配的內存非常多,回收工作也會很艱巨,確定垃圾回收時間間隔就變成了值得思考的問題。
垃圾回收的基本思路:
該過程是周期性的,每隔一個固定的時間,就會自動運行程序。這是一個不能稱之為完美的方案,畢竟算法不能夠准確的知道,我們到底是否需要該變量。
下面以一個函數中局部變量的正常生命周期為例。函數中的局部變量會在函數執行時存在。
此時該變量已經不在需要,占用的內存可以釋放,以供后者使用。所以垃圾回收機制需要跟蹤標記變量,並判定是否使用。如何標記未使用的變量也許有不同的實現方式。但是在瀏覽器里面的話有兩種常用的方式:標記清理和引用計數。
標記清理
在JavaScript中最常用的垃圾回收策略是標記清理(mark-andsweep)。當變量進入上下文,比如:
而不在上下文中的變量,邏輯上講,永遠不應該釋放它們的內存,因為只要上下文中的代碼在運行,就有可能用到它們。
就算變量離開了上下文,也會做出標記。
給變量加標記的方式有很多種。比如,當變量進入上下文時,反轉某一位;或者可以維護“在上下文中”和“不在上下文中”兩個變量列 表,可以把變量從一個列表轉移到另一個列表。標記過程的實現並不重要,關鍵是策略。
垃圾回收程序運行的時候,會標記內存中存儲的所有變量。然后,它會將所有在上下文中的變量,以及被在上下文中的變量引用的變量的標記去掉。在此之后再被加上標記的變量就是待刪除的了,原因是任何在上下文中的變量都訪問不到它們了。隨后垃圾回收程序做一次內存清理,銷毀帶標記的所有值並收回它們的內存。
引用計數
上文提到的第二種垃圾回收機制。另一種沒那么常用的垃圾回收策略是引用計數(reference counting)。
其思路是對每個值都記錄它被引用的次數。聲明變量並給它賦一個引用值時,這個值的引用數為1。如果同一個值又被賦給另一個變量,那么引用數加1。類似地,如果保存對該值引用的變量被其他 值給覆蓋了,那么引用數減1。當一個值的引用數為0時,就說明沒辦 法再訪問到這個值了,因此可以安全地收回其內存了。垃圾回收程序 下次運行的時候就會釋放引用數為0的值的內存。
為什么現在使用引用計數的比較少:該方法無法解決循環引用問題。如:A引用B,同時B引用A,相互應用。會導致內存泄漏。
反反復復提到的內存泄漏是什么?
內存泄漏
是指程序上,動態的分配的堆內存,由於某種原因程序未釋放或無法釋放,造成系統的浪費,導致程序的運行速度減慢,甚至系統崩潰等嚴重后果。
缺陷:具有隱蔽性、積累性的特性、比其他內存非法訪問錯誤。
在內存中共用戶使用的內存空間分為3部分:
1.程序存儲區
2.靜態存儲區
3.動態存儲區
JavaScript中的內存管理
內存:由可讀寫單元組成,表示一片可操作(和內存條中的內存同理)
管理:人為的去操作一片空間的申請,使用和解放
內存管理:開發者主動申請空間,使用空間,釋放空間
管理流程:申請-使用-釋放
JavaScript內存空間分配:
棧:變量 基礎數據類型,值有固定大小(閉包除外)
堆:復雜的對象 引用數據類型的大小是不固定的,引用數據類型的值保持在堆內存的變量中
池:常量
注:JavaScript不允許直接訪問堆內存中的位置
實際上在操作對象的引用,而不是實際的對象
小結
JavaScript是使用垃圾回收的編程語言,開發者不需要操心內存分配和回收。JavaScript的垃圾回收程序可以總結如下。
離開作用域的值會被自動標記為可回收,然后在垃圾回收期間被刪除。
主流的垃圾回收算法是標記清理,即先給當前不使用的值加上標記,再回來回收它們的內存。
引用計數是另一種垃圾回收策略,需要記錄值被引用了多少次。
JavaScript引擎不再使用這種算法,但某些舊版本的IE仍然會受這種算法的影響,原因是JavaScript會訪問非原生JavaScript對象(如 DOM元素)。
引用計數在代碼中存在循環引用時會出現問題。
解除變量的引用不僅可以消除循環引用,而且對垃圾回收也有幫助。
為促進內存回收,全局對象、全局對象的屬性和循環引用都應該在不需要時解除引用