Javascript的世界中,隱藏了不少內存陷阱,不能獲得合理釋放的內存會埋下各類隱患,本文旨在以實用角度去解讀Js涉及到的內存,且看勇士如何鬥惡龍~javascript
本文能夠看作是以前那篇勇士鬥惡龍之沒那麼複雜的Js閉包的後續篇,在思考閉包中內存的問題時,有了寫此文的衝動. 前端
學以至用,從實用的角度出發咱們最須要關注的就是內存回收用什麼用處?咱們平常工做中好像不須要咱們本身去處理Javascript中的內存,它會自動回 收的.若是你問Javascript內存回收,必定會有TX鄙視的告訴你,這個會自動回收,不須要咱們本身處理. java
Javascript中的內存,真的不須要咱們關注嗎??? 程序員
一般這種自問自答的結果,就是會給出一個與問題相反的答案,也就是固然須要!Javascript雖然會自動處理內存,但不是完美的,它存在必定的缺陷.另外一方面,咱們不能由於無知而無畏,由於可能不少莫名其妙的問題就是這樣出現的. 算法
內存-那些咱們忽略的就是咱們要拯救的 編程
在平時的編碼中咱們無時無刻不在執行這樣的一個過程,分配內存 -> 使用內存 -> 回收內存.如此反覆的過程在不斷持續着,每個數字,字符串,對象,數組,方法都佔用着計算機的內存.前端編程由於你的Js代碼大部分運行在客戶端瀏覽 器,因此雖然你沒有佔用服務器的內存,但是你佔用了使用者的內存.即便在硬件配置愈來愈高的今天,也不能徹底保證咱們產品的使用者不是古老的機器,何況你 也應該考慮到移動手機的性能.也不是說每一臺機器都配有4G以上的內存供你揮霍,我一直認爲好的程序員對待內存要保持一個吝嗇的心態. 數組
Javascript在分配內存這一過程當中,會根據不一樣的數據類型進行分配.像基本數據類型會分配在棧內存,引用數據類型分配在堆內存中.引用對於理解內存是一個很重要的概念,我最先對引用的理解來自於C裏面的指針,最好找資料仔細瞭解一下. 瀏覽器
1
2
3
4
|
var
obj = {name:
"benze"
};
var
car = obj;
//這裏car就是指向了obj所建立的那個對象所在的內存空間,也就是說此時obj和car都指向了同一塊內存.
//在內存管理的環境中,若是一個對象有權限去訪問另外一個對象,就叫作一個對象引用另外一個對象
|
咱們平時在使用定義變量,函數或者對象的時候,都在進行各類的內存分配,可是一般不須要寫代碼去回收,由於咱們知道它是自動回收的.這裏我得強調,自動不表明咱們就不要考慮內存回收了. 服務器
使用內存-也就是那些讀讀寫寫
閉包
關於內存的使用,個人理解就是對於已經開闢好內存空間的那些值,進行一些讀寫操做.這一過程當中可能還有對象引用的改變等等,由於這個不是本文重點,不加以贅述.
內存回收 === 屠龍之術?
話說古時候有人散盡家財學的屠龍之術,技成以後卻發現無龍可屠.那Javascript既然有着自動回收的內存管理,咱們學習內存回收豈不也是同樣,反正 它能正常回收就行唄,咱們管它是怎麼回收的呢.可是問題是我以前提到過,Javascript的垃圾回收機制有必定的侷限性和缺陷,有一些狀況會使得內存 得不到釋放而持續增長,這時候咱們就須要人爲的處理它.
引用計數的回收機制
上文提到過引用的概念,Javascript的內存回收的算法主要就依賴於引用,當代碼生成一個新的內存駐留項時(如一個對象),系統就會爲它開闢一塊內 存空間.由於這個對象可能會被傳遞給其餘函數,或者對象.因此可能不少代碼都會指向這個對象的內存空間.javascript的垃圾回收器跟蹤這些指向, 當最後一個指向都被斷開廢棄的時候,這個對象所佔用的空間就會被釋放.
這是一種比較簡單而且清晰的算法,看上去感受沒什麼問題,可是若是出現這種狀況呢?
1
2
3
4
|
var
a = {name:
'a'
};
var
b = {name:
'b'
};
a.bname = b
b.aname = a;
|
代碼看着很彆扭是吧,可是若是真有這種狀況呢,彼此引用.這樣的狀況,javascript的的回收就對a和b沒有辦法了.對於這種循環引用,實在是各種垃圾自動回收的缺陷.
真實狀況的內存沒法回收
也許上面說的那種狀況對於你來講永遠不可能發生,平時注意點也許就避開了,可是總有些狀況,也許你寫了好久本身都沒發現.在稍微舊一點版本的IE下,Javascript的對象是經過標記清除的,BOM和DOM對象倒是經過引用計數,涉及到DOM或者BOM的時候就容易出現循環引用.上代碼瞅瞅:
1
2
3
4
5
6
|
<span></span>$(document).ready(
function
(){
var
div = document.getElementById(
"mydiv"
);
div.onclick =
function
(){
console.log(
"div"
);
}
});<span></span>
|
當指定的單擊事件處理程序時,建立了一個在其封閉的環境中包含div變量的閉包環境.而div也包含一個指向閉包的引用(onclick屬性自身),這就致使了內存都不能獲得釋放.固然解決方法很簡單:
1
2
3
4
5
6
7
|
$(document).ready(
function
(){
var
div = document.getElementById(
"mydiv"
);
div.onclick = saydiv;
});
function
saydiv(){
console.log(
"div"
);
}
|
此時由於saydiv函數不在包含div的引用,因此沒有造成循環,內存能夠獲得釋放.
可能不少人都知道將一個對象置爲null,那麼它的內存就會回收.這是由於變量的指向了一個null,那麼它原來指向的那塊內存空間就會由於沒有被指向,或者說沒有被引用,而被垃圾回收掉.
標記清除-手動內存回收居然仍是屠龍之術?
從2012年起現代瀏覽器中,對於Javascript垃圾回收的機制進行了更新.再也不使用引用計數的算法,而是改成使用標記清除的方式.好比定義一個變 量,那麼當它進入執行環境時,會被垃圾回收器標記爲"進入環境",當其離開環境好比函數執行完畢的時候,標記爲"離開環境".垃圾回收機器就會在這些"離 開環境"的變量中挑選出來須要回收掉的變量用於釋放內存.
這存在一個挑選標準,它不會再去計 算引用的數量.而是從全局對象(根節點)開始尋找,找到全部可得到的對象和全部不可得到的對象.也就是它從以前判斷"對象是否被須要"變成"對象是否能夠 得到".這麼理解,零引用的對象老是不可得到的,可是不可能得到的對象不必定零引用.
如此除了在比較低版本的IE的狀況下,Javascript的自動回收機制就足以應付大多數狀況了.在高級一點的IE中對於內存回收也有很大的進步,因此仍是推薦大多數狀況下不要手工的回收垃圾.
尾筆
原本不打算寫這段尾筆,不過爲了不本文有有始無終的嫌疑,仍是要補充說明一下.首先是大部分現代瀏覽器都已經對內存作了很好的處理,因此大多數狀況下不 須要咱們手工執行.其次本文的目的在於概括總結,而不是非要寫出什麼特殊東東.最後寫此文也是爲了給我本身和看過的人提個醒: