摘要:內(nèi)存管理具有垃圾自動(dòng)回收機(jī)制簡(jiǎn)稱。標(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)境引用計(jì)數(shù)引擎維護(hù)一張引用表,保存內(nèi)存中所有的資源的引用次數(shù)。
JavaScript 內(nèi)存管理
JavaScript 具有垃圾自動(dòng)回收機(jī)制(Garbage Collection)簡(jiǎn)稱 GC。垃圾回收機(jī)制會(huì)中斷整個(gè)代碼執(zhí)行,釋放不可能再被使用的變量,釋放內(nèi)存,這個(gè)工作機(jī)制是周期性的,我們會(huì)在下文詳細(xì)探討。
可釋放對(duì)象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"}
我們對(duì)比上面兩個(gè)函數(shù),fn1 在函數(shù)內(nèi)聲明變量 obj1 并且賦值,在函數(shù)執(zhí)行后這個(gè)變量便不可再訪問了,fn2 在最后把函數(shù)內(nèi)的變量 obj2 返回到全局變量 b,所以 { name: "xiaomuchen", age: "20" } 這個(gè)對(duì)象(或者說 obj2)依然可被訪問。
JavaScript 回收機(jī)制通過判斷變量是否可被訪問,來決定回收哪些變量。
標(biāo)記清除和引用計(jì)數(shù)那么 JavaScript 是如何判斷變量是否可被訪問?這就要提到標(biāo)記清除和引用計(jì)數(shù)。
標(biāo)記清除:標(biāo)記清除是目前大部分 JavaScript 引擎使用的判斷方式,通過標(biāo)記變量的狀態(tài)來確定是否可被回收。當(dāng)變量在環(huán)境中被聲明時(shí)標(biāo)記進(jìn)入環(huán)境,理論上永遠(yuǎn)不要釋放進(jìn)入環(huán)境的變量,因?yàn)樗梢栽诃h(huán)境中的任何位置、任何時(shí)刻被訪問。當(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)境
引用計(jì)數(shù):JavaScript 引擎維護(hù)一張引用表,保存內(nèi)存中所有的資源的引用次數(shù)。資源被引用一次則引用 +1,資源被去掉引用或者退出變量的函數(shù)作用域時(shí),則引用 -1,當(dāng)資源的引用次數(shù)為0時(shí),說明無法訪問這個(gè)值,則等待回收。
(注:引用計(jì)數(shù)從 1 到 0 這個(gè)過程可能不執(zhí)行,而是直接標(biāo)記可被回收,不再進(jìn)行加減運(yù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 } 被釋放
但是引用計(jì)數(shù)存在一種循環(huán)引用的情況,如下例子,兩個(gè)對(duì)象之間相互引用,在離開環(huán)境后對(duì)象不可訪問,但由于對(duì)象的引用次數(shù)為 1,則導(dǎo)致不會(huì)被回收。這個(gè)例子來自《JavaScript 高級(jí)程序設(shè)計(jì)》,但我思考良久,如果引用計(jì)數(shù)把 a.param 也作為一個(gè)變量來計(jì)數(shù),那么就沒有這個(gè)問題了,引用計(jì)數(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 會(huì)中斷代碼執(zhí)行,停止其他操作。因?yàn)橐闅v所有對(duì)象,回收所有不可訪問對(duì)象,這個(gè)操作的耗時(shí)可能有 100ms 以上。在 V8 引擎新版本中引入了兩種優(yōu)化方法:1. 分代回收(Generation GC),2. 增量 GC(increment GC)
分代回收:目的是通過對(duì)象的使用頻率、存在時(shí)長(zhǎng)區(qū)分新生代與老生代對(duì)象。多回收新生代區(qū)(young generation),少回收老生代區(qū)(tenured generation),減少每次需遍歷的對(duì)象,從而減少每次GC的耗時(shí)
增量 GC:把需要長(zhǎng)耗時(shí)的遍歷、回收操作拆分運(yùn)行,減少中斷時(shí)間,但是會(huì)增大上下文切換開銷
Node.js 中的 GC 表現(xiàn)當(dāng)我們用 Node.js 搭建一個(gè)穩(wěn)定的服務(wù)時(shí),就需要考慮服務(wù)器內(nèi)存的開銷,下面一個(gè) Node.js 內(nèi)存回收?qǐng)?zhí)行的例子:
執(zhí)行代碼node --trace_gc --trace_gc_verbose test.js跟蹤一個(gè)網(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) 告訴我們這個(gè)標(biāo)記的步驟分 7 步進(jìn)行,耗時(shí)最長(zhǎng)的一次時(shí) 4.8ms。這使 JavaScript 可以很好的支持開發(fā)高實(shí)時(shí)應(yīng)用,原文。
總結(jié)因?yàn)槠邢?,留下一些小問題供大家思考:
閉包一定會(huì)導(dǎo)致內(nèi)存不可被回收?
如何監(jiān)控一個(gè) Node.js 服務(wù)的內(nèi)存開銷,如何處理不可預(yù)知的內(nèi)存泄漏?
作者:肖沐宸,github。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/89557.html
對(duì)于了解Node的開發(fā)人員,我們都知道Node是基于Chrome V8引擎開發(fā)的能使JavaScript在服務(wù)器端運(yùn)行的運(yùn)行時(shí)環(huán)境(runtime environment)。一方面,它提供了多種可調(diào)用的API,如讀寫文件、網(wǎng)絡(luò)請(qǐng)求、系統(tǒng)信息等。另一方面,因?yàn)镃PU執(zhí)行的是機(jī)器碼,它還負(fù)責(zé)將JavaScript代碼解釋成機(jī)器指令序列執(zhí)行,這部分工作是由V8引擎完成。 Motivation JavaS...
摘要:現(xiàn)在,我們將會(huì)剖析的工作原理,而最重要的是它和在性能方面的比對(duì)加載時(shí)間,執(zhí)行速度,垃圾回收,內(nèi)存使用,平臺(tái)訪問,調(diào)試,多線程以及可移植性。目前,是專門圍繞和的使用場(chǎng)景設(shè)計(jì)的。目前不支持多線程。 原文請(qǐng)查閱這里,略有改動(dòng),本文采用知識(shí)共享署名 4.0 國(guó)際許可協(xié)議共享,BY Troland。 本系列持續(xù)更新中,Github 地址請(qǐng)查閱這里。 這是 JavaScript 工作原理的第六章...
摘要:的內(nèi)存泄漏對(duì)于這門語言的使用者來說,大多數(shù)的使用者的內(nèi)存管理意識(shí)都不強(qiáng)。內(nèi)存泄漏的定義指由于疏忽或錯(cuò)誤造成程序未能釋放已經(jīng)不再使用的內(nèi)存的情況。 javascript的內(nèi)存泄漏 對(duì)于JavaScript這門語言的使用者來說,大多數(shù)的使用者的內(nèi)存管理意識(shí)都不強(qiáng)。因?yàn)镴avaScript一直以來都只作為在網(wǎng)頁上使用的腳本語言,而網(wǎng)頁往往都不會(huì)長(zhǎng)時(shí)間的運(yùn)行,所以使用者對(duì)JavaScript的...
摘要:歡迎來我的博客閱讀開發(fā)者所需要知道的一是一款擁有自動(dòng)垃圾回收功能的編程語言。它隨著的第一版發(fā)布而發(fā)布以及開源。年月,基金宣布和合并,合并版本在未來發(fā)布。年月日,官方公布又一個(gè)新的名為的優(yōu)化編譯器,主要提供的新語法,以及提高性能。 歡迎來我的博客閱讀:「JavaScript 開發(fā)者所需要知道的 V8(一):V8 In NodeJS」 Motivation JavaScript 是一款擁有...
閱讀 1663·2021-08-13 15:03
閱讀 2096·2019-08-30 15:54
閱讀 3554·2019-08-26 10:30
閱讀 1030·2019-08-26 10:22
閱讀 2756·2019-08-23 14:42
閱讀 1815·2019-08-22 11:16
閱讀 1046·2019-08-21 18:33
閱讀 3172·2019-08-21 17:28