摘要:內(nèi)存管理具有垃圾自動回收機制簡稱。標(biāo)記清除標(biāo)記清除是目前大部分引擎使用的判斷方式,通過標(biāo)記變量的狀態(tài)來確定是否可被回收。被標(biāo)記,進(jìn)入環(huán)境被標(biāo)記,進(jìn)入環(huán)境執(zhí)行完畢之后被標(biāo)記,離開環(huán)境引用計數(shù)引擎維護(hù)一張引用表,保存內(nèi)存中所有的資源的引用次數(shù)。
JavaScript 內(nèi)存管理
JavaScript 具有垃圾自動回收機制(Garbage Collection)簡稱 GC。垃圾回收機制會中斷整個代碼執(zhí)行,釋放不可能再被使用的變量,釋放內(nèi)存,這個工作機制是周期性的,我們會在下文詳細(xì)探討。
可釋放對象function fn1() { var obj1 = { name: "xiaomuchen", age: "20" } } function fn2() { var obj2 = { name: "xiaomuchen", age: "20" } return obj2 } var a = fn1() var b = fn2() console.log(a, b) // undefined, {name: "xiaomuchen", age: "20"}
我們對比上面兩個函數(shù),fn1 在函數(shù)內(nèi)聲明變量 obj1 并且賦值,在函數(shù)執(zhí)行后這個變量便不可再訪問了,fn2 在最后把函數(shù)內(nèi)的變量 obj2 返回到全局變量 b,所以 { name: "xiaomuchen", age: "20" } 這個對象(或者說 obj2)依然可被訪問。
JavaScript 回收機制通過判斷變量是否可被訪問,來決定回收哪些變量。
標(biāo)記清除和引用計數(shù)那么 JavaScript 是如何判斷變量是否可被訪問?這就要提到標(biāo)記清除和引用計數(shù)。
標(biāo)記清除:標(biāo)記清除是目前大部分 JavaScript 引擎使用的判斷方式,通過標(biāo)記變量的狀態(tài)來確定是否可被回收。當(dāng)變量在環(huán)境中被聲明時標(biāo)記進(jìn)入環(huán)境,理論上永遠(yuǎn)不要釋放進(jìn)入環(huán)境的變量,因為它可以在環(huán)境中的任何位置、任何時刻被訪問。當(dāng)環(huán)境被銷毀(如函數(shù)執(zhí)行完),則變量被標(biāo)記離開環(huán)境等待回收。
function fn(){ var a = { count: 10 } // 被標(biāo)記,進(jìn)入環(huán)境 var b = { count: 20 } // 被標(biāo)記,進(jìn)入環(huán)境 } fn(); // 執(zhí)行完畢之后 b 被標(biāo)記,離開環(huán)境
引用計數(shù):JavaScript 引擎維護(hù)一張引用表,保存內(nèi)存中所有的資源的引用次數(shù)。資源被引用一次則引用 +1,資源被去掉引用或者退出變量的函數(shù)作用域時,則引用 -1,當(dāng)資源的引用次數(shù)為0時,說明無法訪問這個值,則等待回收。
(注:引用計數(shù)從 1 到 0 這個過程可能不執(zhí)行,而是直接標(biāo)記可被回收,不再進(jìn)行加減運算節(jié)約開銷)
function fn(){ var a = { count: 10 } // 資源 { count: 10 } 被引用次數(shù)為 1 a = { count: 20 } // 資源 { count: 20 } 被引用次數(shù)為 1,資源 { count: 10 } 被引用次數(shù)為 0,等待回收 // do someThing } fn(); // 資源 { count: 20 } 被釋放
但是引用計數(shù)存在一種循環(huán)引用的情況,如下例子,兩個對象之間相互引用,在離開環(huán)境后對象不可訪問,但由于對象的引用次數(shù)為 1,則導(dǎo)致不會被回收。這個例子來自《JavaScript 高級程序設(shè)計》,但我思考良久,如果引用計數(shù)把 a.param 也作為一個變量來計數(shù),那么就沒有這個問題了,引用計數(shù)實現(xiàn)的方式不同,產(chǎn)生的結(jié)果也不一樣。
function fn(){ var a = { count: 10 } var b = { count: 20 } a.param = b // b 的引用次數(shù)為 2 b.param = a // a 的引用次數(shù)為 2 } fn(); // a、b 的引用次數(shù)為 1GC 的缺陷、分代回收和增量 GC
和其他語言一樣 GC 會中斷代碼執(zhí)行,停止其他操作。因為要遍歷所有對象,回收所有不可訪問對象,這個操作的耗時可能有 100ms 以上。在 V8 引擎新版本中引入了兩種優(yōu)化方法:1. 分代回收(Generation GC),2. 增量 GC(increment GC)
分代回收:目的是通過對象的使用頻率、存在時長區(qū)分新生代與老生代對象。多回收新生代區(qū)(young generation),少回收老生代區(qū)(tenured generation),減少每次需遍歷的對象,從而減少每次GC的耗時
增量 GC:把需要長耗時的遍歷、回收操作拆分運行,減少中斷時間,但是會增大上下文切換開銷
Node.js 中的 GC 表現(xiàn)當(dāng)我們用 Node.js 搭建一個穩(wěn)定的服務(wù)時,就需要考慮服務(wù)器內(nèi)存的開銷,下面一個 Node.js 內(nèi)存回收執(zhí)行的例子:
執(zhí)行代碼node --trace_gc --trace_gc_verbose test.js跟蹤一個網(wǎng)絡(luò)服務(wù)的 GC。
[41204:0x102001c00] Memory reducer: call rate 0.056, low alloc, foreground [41204:0x102001c00] Memory reducer: started GC #1 [41204:0x102001c00] Heap growing factor 1.1 based on mu=0.970, speed_ratio=42956 (gc=675253, mutator=16) [41204:0x102001c00] Grow: old size: 21382 KB, new limit: 33604 KB (1.1) [41204:0x102001c00] Memory reducer: finished GC #1 (will do more) [41204:0x102001c00] 156410 ms: Mark-sweep 27.7 (50.0) -> 21.0 (30.0) MB, 12.4 / 0.0 ms (+ 20.4 ms in 7 steps since start of marking, biggest step 4.8 ms) [Incremental marking task: finalize incremental marking] [GC in old space requested]. [41204:0x102001c00] Memory allocator, used: 30756 KB, available: 1435612 KB [41204:0x102001c00] New space, used: 169 KB, available: 838 KB, committed: 1024 KB [41204:0x102001c00] Old space, used: 16662 KB, available: 2417 KB, committed: 19412 KB [41204:0x102001c00] Code space, used: 4078 KB, available: 178 KB, committed: 5120 KB [41204:0x102001c00] Map space, used: 642 KB, available: 0 KB, committed: 2128 KB [41204:0x102001c00] Large object space, used: 0 KB, available: 1434571 KB, committed: 0 KB [41204:0x102001c00] All spaces, used: 21552 KB, available: 1438005 KB, committed: 27684 KB [41204:0x102001c00] External memory reported: 1026 KB [41204:0x102001c00] Total time spent in GC : 158.6 ms [41204:0x102001c00] Memory reducer: call rate 0.003, low alloc, foreground
首先我們可以看到 Node.js 區(qū)分 New space、Old space 等來劃分檢索空間。而提示(+ 20.4 ms in 7 steps since start of marking, biggest step 4.8 ms) 告訴我們這個標(biāo)記的步驟分 7 步進(jìn)行,耗時最長的一次時 4.8ms。這使 JavaScript 可以很好的支持開發(fā)高實時應(yīng)用,原文。
總結(jié)因為篇幅有限,留下一些小問題供大家思考:
閉包一定會導(dǎo)致內(nèi)存不可被回收?
如何監(jiān)控一個 Node.js 服務(wù)的內(nèi)存開銷,如何處理不可預(yù)知的內(nèi)存泄漏?
作者:肖沐宸,github。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/89557.html
對于了解Node的開發(fā)人員,我們都知道Node是基于Chrome V8引擎開發(fā)的能使JavaScript在服務(wù)器端運行的運行時環(huán)境(runtime environment)。一方面,它提供了多種可調(diào)用的API,如讀寫文件、網(wǎng)絡(luò)請求、系統(tǒng)信息等。另一方面,因為CPU執(zhí)行的是機器碼,它還負(fù)責(zé)將JavaScript代碼解釋成機器指令序列執(zhí)行,這部分工作是由V8引擎完成。 Motivation JavaS...
摘要:現(xiàn)在,我們將會剖析的工作原理,而最重要的是它和在性能方面的比對加載時間,執(zhí)行速度,垃圾回收,內(nèi)存使用,平臺訪問,調(diào)試,多線程以及可移植性。目前,是專門圍繞和的使用場景設(shè)計的。目前不支持多線程。 原文請查閱這里,略有改動,本文采用知識共享署名 4.0 國際許可協(xié)議共享,BY Troland。 本系列持續(xù)更新中,Github 地址請查閱這里。 這是 JavaScript 工作原理的第六章...
摘要:的內(nèi)存泄漏對于這門語言的使用者來說,大多數(shù)的使用者的內(nèi)存管理意識都不強。內(nèi)存泄漏的定義指由于疏忽或錯誤造成程序未能釋放已經(jīng)不再使用的內(nèi)存的情況。 javascript的內(nèi)存泄漏 對于JavaScript這門語言的使用者來說,大多數(shù)的使用者的內(nèi)存管理意識都不強。因為JavaScript一直以來都只作為在網(wǎng)頁上使用的腳本語言,而網(wǎng)頁往往都不會長時間的運行,所以使用者對JavaScript的...
摘要:歡迎來我的博客閱讀開發(fā)者所需要知道的一是一款擁有自動垃圾回收功能的編程語言。它隨著的第一版發(fā)布而發(fā)布以及開源。年月,基金宣布和合并,合并版本在未來發(fā)布。年月日,官方公布又一個新的名為的優(yōu)化編譯器,主要提供的新語法,以及提高性能。 歡迎來我的博客閱讀:「JavaScript 開發(fā)者所需要知道的 V8(一):V8 In NodeJS」 Motivation JavaScript 是一款擁有...
閱讀 1827·2021-08-13 15:03
閱讀 2199·2019-08-30 15:54
閱讀 3633·2019-08-26 10:30
閱讀 1131·2019-08-26 10:22
閱讀 2846·2019-08-23 14:42
閱讀 1891·2019-08-22 11:16
閱讀 1120·2019-08-21 18:33
閱讀 3261·2019-08-21 17:28