JavaScript中的内存释放
C、C++語言需要手動管理內存的分配與釋放(常用方法:malloc(), calloc(), realloc()和free()等)。而JavaScript與Java、C#相似,內置了垃圾回收器,能自動管理內存的分配與釋放。
內存生命周期:
分配內存
使用分配的內存(讀與寫操作)
當應用程序不再需要時,釋放掉已分配的內存
雖然垃圾回收器能能自動管理內存分配、釋放,但并不意味著開發者不再需要關注內存管理。因為一些不好的編碼會導致內存泄露,即應用程序不再需要的內存沒有被釋放掉。因此了解內存管理是很重要的。
Javascript中的內存分配
當聲明變量時,JavaScript會自動為變量分配內存
var numberVar = 100; //為整數分配內存
var stringVar = 'node simplified'; // 為字符串分配內存
var objectVar = {a: 1}; // 為對象分配內存
var a = [1, null, 'abra']; // 為數組分配內存
function f(a) {
return a + 2;
} // 為函數分配內存
GC(Garbage collection)
垃圾回收是追蹤并釋放應用程序不再使用的內存過程。垃圾回收器通過算法來實現追蹤應用程序不再使用的內存。主要涉及的垃圾回收算法如下:
Reference-counting garbage collection(引用計數)
Mark-and-sweep algorithm(標記清除)
Reference-counting garbage collection(引用計數)
引用計數算法是一種最基礎的垃圾回收算法,當一個對象的引用數為零時,會被自動回收。該算法將一個對象的引用數為0時視為應用程序不再需要的內存。
!function (){
var o1 = {a: {b: 2}},// 兩個對象被創建。假如分別用A:{a: {b: 2}},B:{b: 2}表示,對象B被對象A的屬性a引用,對象A被賦值給變量o1。A和B的引用數都為1,因此不能被回收。
o2 = o1; // 將對象A賦給變量o2。此時A引用數為2,B引用數1。
o1 = 1;// 將變量o1對對象A引用切斷。此時A引用數為1,B引用數1。
var oa = o2.a; // 將對象B賦值給變量oa。此時A引用數為1,B引用數2。
o2 = 'foo'; // 將變量o2對對象A引用切斷。此時A引用數為0,B引用數1。因為對象A的a屬性被變量oa引用,因此對象A不能被釋放。
oa = null; // 將變量oa對對象B引用切斷。此時A引用數為0,B引用數0。A與B會被回收。
}()
引用計數的限制:循環引用
循環引用存在一個限制。如下實例,兩個對象相互引用,形成一個循環引用。正常情況下,當函數執行完后,對應的內存會被釋放掉。而引用計數算法會將循環引用對象的引用數都視為至少為1,因此不能被回收。
function f() {
var o = {};
var o2 = {};
o.a = o2; // o references o2
o2.a = o; // o2 references o
return 'azerty';
}
f();
常見問題
IE6-7的DOM對象是基于計數引用算法進行垃圾回收的。而循環引用通常會導致內存泄露:
var div;
window.onload = function() {
div = document.getElementById('myDivElement');
div.circularReference = div;
div.lotsOfData = new Array(10000).join('*');
};
如上述實例,DOM元素div通過自身的“circularReference”屬性循環引用自己。如果沒有顯式將該屬性刪除或設為null,計數引用垃圾回收器會始終持有至少一個引用。即使DOM元素從DOM樹種移除,DOM元素的內存會一直存在。如果DOM元素持有一些數據(如實例中“lotsData”屬性),該數據對應的內存也無法被釋放。了解更多參考--->IE<8循環引用導致的內存泄露
Mark-and-sweep algorithm(標記清除)
該算法將“對象不再需要”的定義簡化為“對象不可到達”。 這個算法假設有一組被稱為roots的對象(在JavaScript中,root就是全局對象)。垃圾回收器會定期地從這些roots開始,查找所有從根開始引用的對象,然后再查找這些對象引用的對象……。從roots開始,垃圾回收器會查找所有可到達對象,并回收不可到達的對象。
為了確定對象是否需要,該算法要確定對象是否可到達。由如下步驟組成:
垃圾回收器會創建一組roots,roots通常是持有引用的全局變量。在JavaScript中,window對象就可作為root的全局變量。
垃圾回收器會檢查所有的roots并標記為活躍狀態。然后遞歸遍歷所有的子變量。只要從root不能到達的都被標記為垃圾。
所有沒有被標記為活躍狀態的內存塊都被視為垃圾。垃圾回收器就可以釋放這部分內存并把釋放的內存返回給操作系統。
這個算法比引用計數算法更優,因為對于引用計數算法“零引用的對象”總是不可到達的,但反之則不一定,如循環引用。而標記清除算法不存在循環引用的問題。
截至2012年,所有現代瀏覽器都內置了標記清除垃圾回收器。在過去幾年里所有對JavaScript垃圾回收算法的改進(generational/incremental/concurrent/parallel garbage collection)都是基于標記清除算法來實現的,但并沒有改變標記清除算法本身和它對“對象不再需要”定義的簡化。
循環引用不再是問題
前面循環引用的實例中,在函數執行完后,兩個對象不再被全局對象可訪問的對象引用。因此這兩個對象被垃圾回收器標記為不可到達,接著被回收掉。
限制:需要明確無法到達的對象
對于這個限制,實踐中很少遇見,所以開發者不太會去關心垃圾回收機制。
參考文章:
Memory Management And Garbage Collection In Javascript
Memory Management
How JavaScript works: memory management + how to handle 4 common memory leaks
Memory Management Reference
原文地址:https://github.com/cucygh/js-leakage-patterns/blob/master/JavaScript%E5%86%85%E5%AD%98%E9%82%A3%E7%82%B9%E4%BA%8B/JavaScript%E5%86%85%E5%AD%98%E9%82%A3%E7%82%B9%E4%BA%8B.md
總結
以上是生活随笔為你收集整理的JavaScript中的内存释放的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: caffe2 介绍
- 下一篇: 如何向IPython Notebook中