DIV CSS 佈局教程網

 DIV+CSS佈局教程網 >> 網頁腳本 >> JavaScript入門知識 >> JavaScript基礎知識 >> JavaScript對內存分配及管理機制詳細解析
JavaScript對內存分配及管理機制詳細解析
編輯:JavaScript基礎知識     

你可能聽說過JAVA、.NET、PHP這些語言有垃圾回收的內存管理機制,但是很少會聽到JavaScript也有自己的內存管理機制,JavaScript同樣有著類似的垃圾回收功能。本文主要講述了JavaScript的垃圾回收原理和具體的過程。

簡介
在底層語言中,比如C,有專門的內存管理機制,比如malloc() 和 free()。而Javascript是有垃圾回收(garbage collection)機制的,也就是說JS解釋器會自動分配和回收內存。這樣就有人覺得,我用的是高級語言,就不用關心內存管理了,其實這是不對的。

內存的生命周期
盡管語言不盡相同,而每種語言中內存的生命周期都是相似的:

1.當需要的時候分配內存
2.對內存進行讀寫操作
3.當上面分配的內存不再需要的時候,將他們釋放掉
對於1,2兩步,幾乎所有語言操作起來都是明確地或者說很直觀,沒什麼好說的。而在像Javascript一樣的高級語言中,第三步操作就顯得不那麼直觀。

Javascript中分配內存空間
變量初始化
當變量初始化的時候,Javascript會自動分配相應的內存空間(注:這裡MDN上關於這裡用的是Value initialization,到底是聲明,還是在賦值時候分配空間,還要再學習一下)

var n = 123; //  為數字分配空間
var s = “azerty”; // 字符串

var o = {
a: 1,
b: null
}; // 為對象和它包含的屬性分配內存空間

var a = [1, null, "abra"]; // (類似對象)給數組和它裡面的元素分配空間

function f(a){
return a + 2;
} // 為函數分配空間

//  函數有時也會為分配對象空間
someElement.addEventListener(‘click', function(){
someElement.style.backgroundColor = ‘blue'; //個人補充,未考證,這裡會為someElement分配空間,如注釋所說,為對象分配空間
}, false);

函數調用時候分配空間
有的函數調用,會產生上面說的那種 為對象分配空間

var d = new Date();
var e = document.createElement('div'); // allocates an DOM element還有下面這種

var s = “azerty”;
var s2 = s.substr(0, 3); // s2 is a new string
// 由於Javascript中字符串是不可變的,所以Javascript也許並沒有為s2中的字符串分配新空間,而是只存了[0, 3]的區間(用來索引)

var a = ["ouais ouais", "nan nan"];
var a2 = ["generation", "nan nan"];
var a3 = a.concat(a2); // 新的空間來存儲數組a3

操作變量值
沒什麼好說的,讀、寫、函數調用。

內存不再被使用時,將它們釋放掉
許多內存管理機制的問題都出現在這裡。最麻煩的問題是確認“這塊內存空間已經不需要了”。這往往需要程序員告知,這個程序中,這塊內存已經不需要了,你們回收吧。

而高級語言解釋器中嵌入了一個叫做“垃圾回收(garbage collector)”的工具,用來跟蹤內存分配和使用情況,以便在它們不需要的時候將其自動回收。然而有個問題,一塊內存空間是不是還有用,是具有不確定性的,也就是說,這個是沒法用算法精確算出來的。

垃圾回收
如上所述原因,垃圾回收機制采取了一種有限的解決方案來處理上面的不確定性問題。下面介紹集中垃圾回收算法的思想以及相應的局限:

引用
這種方法,用到了一種引用的思想。當a能訪問A時,就說A引用了a(不論是直接還是間接的)。比如,一個Javascript對象會引用他的原型(間接引用)和它的各個屬性(直接引用)。

這種情形下,對象就被擴展的更廣義了,在原生對象的基礎上,還包含了函數的作用域鏈(或者全局的詞法作用域)。

引用計數
這種方法是最拿衣服(naive)的垃圾回收算法。它把“可以回收”的標准定義為“沒有其他人引用這個對象”(原文:This algorithm reduces the definition of “an object is not needed anymore” to “an object has no other object referencing to it”)。也就是說,只有當對象沒有被引用的時候,才會被當作垃圾回收掉。

舉個例子
var o = { // 稱之為外層對象
a: { //稱之為內層對象
b:2
}
}; //  創建了兩個對象 內層對象作為外層對象的屬性而被引用
// 而外層對象被變量o引用
// 顯然,沒有人會被垃圾回收

var o2 = o; // o2也引用了上面說的外層對象。好現在外層對象的引用計數為‘2' (被o和o2引用)
o = 1; //  現在o不再引用外層對象,只有o2在引用,引用計數為 ‘1'

var oa = o2.a; // oa 引用內層對象
//  現在內層對象同時被作為外層對象的屬性引用和被oa引用,引用計數為‘2'

o2 = “yo”; //  好,現在o2也不引用外層對象了,外層對象引用計數為“0”
// 意味著外層對象可以被“垃圾回收”了
// 然而,內層對象還被oa引用著,因此還是沒有被回收 (個人注釋:這裡有一點閉包的意味)

oa = null; //  現在oa不引用內層對象了
// 內層對象也被垃圾回收

局限:循環引用

看下面代碼:

function f(){
var o = {};
var o2 = {};
o.a = o2; // o 引用 o2
o2.a = o; // o2 引用 o

return “azerty”;
}

f();
// o o2兩個對象構成了循環引用
// 當函數執行完畢的時候,他們就被關在了f的作用域裡面,沒有外面的人可以使用他們
// 所以按理說,他們已經沒有存在價值了,需要被垃圾回收,釋放內存
// 然而,他們的引用計數都不為“0”
// 所以在這種引用計數的機制下,他們沒有被回收

實際例子
在IE6,7版本的浏覽器中,就是使用的引用計數機制。因此,下面的代碼在IE6,7中可以穩穩地發生內存洩漏

var div = document.createElement("div");
div.onclick = function(){
  doSomething();
}; // div的onclick屬性,會引用 function
// 然而這個 function 反過來又引用了這個div,因為div在handler的作用域裡面。
// 造成上述循環引用,導致內存洩漏。標記清除算法

這種算法把“可以回收”定義成“對象不可達”,即訪問不到。

這種算法,會定義一個“根”,並且定期地從“根”出發,找出“根”下面的所有對象,看能不能從“根”找到一條路徑引用到這個對象。從不同的“根”出發,垃圾回收程序就可以區分所有對象是不是“不可達”的,當對象“不可達”時候,便被回收。

這種算法比引用計數算法要好些。因為 “一個對象的引用計數是0”可以推出“這個對象不可達”,逆命題則為假。也就是說這種算法擴充了垃圾回收的范圍。

循環引用不再是困擾
在上面的循環引用例子中,當函數返回時,o 和 o2都已經不再被任何人引用,也就是“不可達”了,便順理成章地被垃圾回收掉了。

局限:對象需要明確的“不可達”
雖然說是局限,然而這種情況在實際當中很少發生,因此很少有人關注這一點。

XML學習教程| jQuery入門知識| AJAX入門| Dreamweaver教程| Fireworks入門知識| SEO技巧| SEO優化集錦|
Copyright © DIV+CSS佈局教程網 All Rights Reserved