摘要:內存管理具有垃圾自動回收機制簡稱。標記清除標記清除是目前大部分引擎使用的判斷方式,通過標記變量的狀態來確定是否可被回收。被標記,進入環境被標記,進入環境執行完畢之后被標記,離開環境引用計數引擎維護一張引用表,保存內存中所有的資源的引用次數。
JavaScript 內存管理
JavaScript 具有垃圾自動回收機制(Garbage Collection)簡稱 GC。垃圾回收機制會中斷整個代碼執行,釋放不可能再被使用的變量,釋放內存,這個工作機制是周期性的,我們會在下文詳細探討。
可釋放對象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"}
我們對比上面兩個函數,fn1 在函數內聲明變量 obj1 并且賦值,在函數執行后這個變量便不可再訪問了,fn2 在最后把函數內的變量 obj2 返回到全局變量 b,所以 { name: "xiaomuchen", age: "20" } 這個對象(或者說 obj2)依然可被訪問。
JavaScript 回收機制通過判斷變量是否可被訪問,來決定回收哪些變量。
標記清除和引用計數那么 JavaScript 是如何判斷變量是否可被訪問?這就要提到標記清除和引用計數。
標記清除:標記清除是目前大部分 JavaScript 引擎使用的判斷方式,通過標記變量的狀態來確定是否可被回收。當變量在環境中被聲明時標記進入環境,理論上永遠不要釋放進入環境的變量,因為它可以在環境中的任何位置、任何時刻被訪問。當環境被銷毀(如函數執行完),則變量被標記離開環境等待回收。
function fn(){ var a = { count: 10 } // 被標記,進入環境 var b = { count: 20 } // 被標記,進入環境 } fn(); // 執行完畢之后 b 被標記,離開環境
引用計數:JavaScript 引擎維護一張引用表,保存內存中所有的資源的引用次數。資源被引用一次則引用 +1,資源被去掉引用或者退出變量的函數作用域時,則引用 -1,當資源的引用次數為0時,說明無法訪問這個值,則等待回收。
(注:引用計數從 1 到 0 這個過程可能不執行,而是直接標記可被回收,不再進行加減運算節約開銷)
function fn(){ var a = { count: 10 } // 資源 { count: 10 } 被引用次數為 1 a = { count: 20 } // 資源 { count: 20 } 被引用次數為 1,資源 { count: 10 } 被引用次數為 0,等待回收 // do someThing } fn(); // 資源 { count: 20 } 被釋放
但是引用計數存在一種循環引用的情況,如下例子,兩個對象之間相互引用,在離開環境后對象不可訪問,但由于對象的引用次數為 1,則導致不會被回收。這個例子來自《JavaScript 高級程序設計》,但我思考良久,如果引用計數把 a.param 也作為一個變量來計數,那么就沒有這個問題了,引用計數實現的方式不同,產生的結果也不一樣。
function fn(){ var a = { count: 10 } var b = { count: 20 } a.param = b // b 的引用次數為 2 b.param = a // a 的引用次數為 2 } fn(); // a、b 的引用次數為 1GC 的缺陷、分代回收和增量 GC
和其他語言一樣 GC 會中斷代碼執行,停止其他操作。因為要遍歷所有對象,回收所有不可訪問對象,這個操作的耗時可能有 100ms 以上。在 V8 引擎新版本中引入了兩種優化方法:1. 分代回收(Generation GC),2. 增量 GC(increment GC)
分代回收:目的是通過對象的使用頻率、存在時長區分新生代與老生代對象。多回收新生代區(young generation),少回收老生代區(tenured generation),減少每次需遍歷的對象,從而減少每次GC的耗時
增量 GC:把需要長耗時的遍歷、回收操作拆分運行,減少中斷時間,但是會增大上下文切換開銷
Node.js 中的 GC 表現當我們用 Node.js 搭建一個穩定的服務時,就需要考慮服務器內存的開銷,下面一個 Node.js 內存回收執行的例子:
執行代碼node --trace_gc --trace_gc_verbose test.js跟蹤一個網絡服務的 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 區分 New space、Old space 等來劃分檢索空間。而提示(+ 20.4 ms in 7 steps since start of marking, biggest step 4.8 ms) 告訴我們這個標記的步驟分 7 步進行,耗時最長的一次時 4.8ms。這使 JavaScript 可以很好的支持開發高實時應用,原文。
總結因為篇幅有限,留下一些小問題供大家思考:
閉包一定會導致內存不可被回收?
如何監控一個 Node.js 服務的內存開銷,如何處理不可預知的內存泄漏?
作者:肖沐宸,github。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/89557.html
對于了解Node的開發人員,我們都知道Node是基于Chrome V8引擎開發的能使JavaScript在服務器端運行的運行時環境(runtime environment)。一方面,它提供了多種可調用的API,如讀寫文件、網絡請求、系統信息等。另一方面,因為CPU執行的是機器碼,它還負責將JavaScript代碼解釋成機器指令序列執行,這部分工作是由V8引擎完成。 Motivation JavaS...
摘要:現在,我們將會剖析的工作原理,而最重要的是它和在性能方面的比對加載時間,執行速度,垃圾回收,內存使用,平臺訪問,調試,多線程以及可移植性。目前,是專門圍繞和的使用場景設計的。目前不支持多線程。 原文請查閱這里,略有改動,本文采用知識共享署名 4.0 國際許可協議共享,BY Troland。 本系列持續更新中,Github 地址請查閱這里。 這是 JavaScript 工作原理的第六章...
摘要:的內存泄漏對于這門語言的使用者來說,大多數的使用者的內存管理意識都不強。內存泄漏的定義指由于疏忽或錯誤造成程序未能釋放已經不再使用的內存的情況。 javascript的內存泄漏 對于JavaScript這門語言的使用者來說,大多數的使用者的內存管理意識都不強。因為JavaScript一直以來都只作為在網頁上使用的腳本語言,而網頁往往都不會長時間的運行,所以使用者對JavaScript的...
摘要:歡迎來我的博客閱讀開發者所需要知道的一是一款擁有自動垃圾回收功能的編程語言。它隨著的第一版發布而發布以及開源。年月,基金宣布和合并,合并版本在未來發布。年月日,官方公布又一個新的名為的優化編譯器,主要提供的新語法,以及提高性能。 歡迎來我的博客閱讀:「JavaScript 開發者所需要知道的 V8(一):V8 In NodeJS」 Motivation JavaScript 是一款擁有...
閱讀 1658·2021-08-13 15:03
閱讀 2089·2019-08-30 15:54
閱讀 3549·2019-08-26 10:30
閱讀 1025·2019-08-26 10:22
閱讀 2752·2019-08-23 14:42
閱讀 1813·2019-08-22 11:16
閱讀 1045·2019-08-21 18:33
閱讀 3167·2019-08-21 17:28