摘要:正好最近在學(xué)習(xí)的各種實現(xiàn)原理,在這里斗膽翻譯一篇垃圾回收機制原文鏈接。自動管理的機制中,通常都會包含垃圾回收機制。二垃圾回收機制的概念垃圾回收,是一種自動管理應(yīng)用程序所占內(nèi)存的機制,簡稱方便起見,本文均采用此簡寫。
最近關(guān)注了一個國外技術(shù)博客RisingStack里面有很多高質(zhì)量,且對新手也很friendly的文章。正好最近在學(xué)習(xí)Node.js的各種實現(xiàn)原理,在這里斗膽翻譯一篇Node.js垃圾回收機制(原文鏈接)。
正文在這篇文章中,你將會學(xué)習(xí)Node.js的垃圾回收(garbege collection)機制是如何工作的;即在你敲代碼的時候,后臺是怎么幫你清空內(nèi)存里的垃圾的。
一、Node.js應(yīng)用的內(nèi)存管理內(nèi)存的適當(dāng)分配,對于所有應(yīng)用都至關(guān)重要。內(nèi)存管理的任務(wù),就是在程序請求內(nèi)存的時候動態(tài)地為它們分配內(nèi)存塊;并在程序不再需要內(nèi)存的時候釋放掉。
應(yīng)用級別的內(nèi)存管理,有手動管理和自動管理兩種模式。自動管理的機制中,通常都會包含垃圾回收機制。
The following code snippet shows how memory can be allocated in C, using manual memory management:
下面的代碼片段展示了C語言中內(nèi)存是如何分配的,這屬于手動管理:
#include#include #include int main() { char name[20]; char *description; strcpy(name, "RisingStack"); // memory allocation description = malloc( 30 * sizeof(char) ); if( description == NULL ) { fprintf(stderr, "Error - unable to allocate required memory "); } else { strcpy( description, "Trace by RisingStack is an APM."); } printf("Company name = %s ", name ); printf("Description: %s ", description ); // release memory free(description); }
在手動內(nèi)存管理機制中,釋放無用內(nèi)存的任務(wù)落在了程序猿身上。這樣可能會給應(yīng)用帶來嚴(yán)重的問題:
內(nèi)存泄露:可能某些占用的內(nèi)存一直沒有被釋放。
當(dāng)一個對象被刪除(過早釋放)的時候,可能會有指針不指向任何有效的對象,但仍然指向原來的內(nèi)存。這種指針被稱為“懸掛指針”。這個時候如果再去使用這段內(nèi)存,就會產(chǎn)生嚴(yán)重的安全問題。
但幸運的是,Node.js是自帶垃圾回收機制的,所以你不需要手動管理內(nèi)存。
二、垃圾回收機制的概念垃圾回收,是一種自動管理應(yīng)用程序所占內(nèi)存的機制,簡稱“GC”(方便起見,本文均采用此簡寫)。它的任務(wù),就是回收無用對象(即垃圾)所占用的內(nèi)存。它第一次出現(xiàn),是在1959年的LISP語言中,由John McCarthy發(fā)明。
GC判斷一個對象為垃圾的標(biāo)準(zhǔn)是:是否還有其他對象引用它。
如果沒有GCThe way how the GC knows that objects are no longer in use is that no
other object has references to them.
下圖展示了沒有垃圾管理機制的時候,內(nèi)存的情況。可以看到有的對象與其余的對象之間,沒有任何引用關(guān)系,但他們的內(nèi)存也不會被回收。
有了GC之后有了GC之后,沒有引用關(guān)系的對象占用的內(nèi)存,都會被GC悄然回收。
使用GC的優(yōu)勢it prevents wild/dangling pointers bugs,
it won"t try to free up space that was already freed up,
it will protect you from some types of memory leaks.
Of course, using a garbage collector doesn"t solve all of your problems, and it’s not a silver bullet for memory management. Let"s take a look at things that you should keep in mind!
避免了懸掛指針的出現(xiàn)。
它不會嘗試去重復(fù)釋放并沒有被占用的內(nèi)存。
它會防止某些類型的內(nèi)存泄露。
當(dāng)然了,GC并不能解決所有內(nèi)存相關(guān)的問題,它不是解決內(nèi)存管理問題的萬金油。有些使用GC的注意事項還是需要開發(fā)者牢記:
performance impact - in order to decide what can be freed up, the GC consumes computing power
unpredictable stalls - modern GC implementations try to avoid "stop-the-world" collections
對性能的影響:在判斷哪些內(nèi)存要釋放的時候,GC會占用CPU資源。
不可預(yù)測的中斷:盡管現(xiàn)在的GC都會避免“停止一切”的情況發(fā)生,但是還是不可避免的會出現(xiàn)。
譯注:“停止一切”(stop-the-world)是指當(dāng)垃圾收集沒有結(jié)束前,內(nèi)存對于外部的請求是不會進行響應(yīng)的,直到收集完畢應(yīng)用才會繼續(xù)響應(yīng)請求。
三、Node.js垃圾回收&內(nèi)存管理實踐學(xué)代碼就是要寫代碼,下面就用幾段代碼展示本節(jié)的主題。首先介紹幾個基本概念:
棧(Stack)棧中存儲著本地變量、指向堆中對象的指針、定義應(yīng)用程序控制流的指針。
在下面的例子中,變量a、b都會存儲在棧中。
function add (a, b) { return a + b } add(4, 5)堆(Heap)
堆專門用于存儲“引用類型”的對象,例如字符串或?qū)ο蟆?/p>
下例中的Car對象就是保存在堆中的。
function Car (opts) { this.name = opts.name } const LightningMcQueen = new Car({name: "Lightning McQueen"})
執(zhí)行之后,內(nèi)存看上去會是這個樣子:
新建更多的Car對象的話,內(nèi)存會變成這樣:
function Car (opts) { this.name = opts.name } const LightningMcQueen = new Car({name: "Lightning McQueen"}) const SallyCarrera = new Car({name: "Sally Carrera"}) const Mater = new Car({name: "Mater"})
如果這個時候執(zhí)行垃圾回收,那么什么都不會發(fā)生,因為根對象(root)對每個對象都有引用。
那現(xiàn)在把上述例子再復(fù)雜化一點,給Car對象添加點“部件”。
function Engine (power) { this.power = power } function Car (opts) { this.name = opts.name this.engine = new Engine(opts.power) } let LightningMcQueen = new Car({name: "Lightning McQueen", power: 900}) let SallyCarrera = new Car({name: "Sally Carrera", power: 500}) let Mater = new Car({name: "Mater", power: 100})
What would happen, if we no longer use Mater, but redefine it and assign some other value, like Mater = undefined?
現(xiàn)在,如果我們不想再使用Mater這個實例,把他賦一個別的值,比如Mater = undefined。這時會發(fā)生什么?
可以看到Mater失去了root對他的引用。那么,在下次垃圾回收執(zhí)行的時候,它的內(nèi)存就會被釋放。
好了,現(xiàn)在我們都理解了GC的基本原理和執(zhí)行方式,來看看V8引擎中的GC是如何實現(xiàn)的吧!
垃圾回收方法在我們之前的一篇文章中,我們介紹過Node.js的垃圾回收方法是如何工作的,我強烈建議閱讀此文章。
這篇文章的要點如下:
堆中存在兩個“段”(segment),新生代空間(New Space)和老生代空間(Old Space)。新的內(nèi)存分配都發(fā)生在新建空間中,它只有1-8MBs左右大,但垃圾回收卻很迅速和頻繁。這里存儲的對象稱為“新生代”(Young Generation)。
老生代空間中,存儲著那些新生代空間中未被回收,晉升至此的對象。它們被稱為“老生代”(Old Generation)。這里內(nèi)存分配非常頻繁,但垃圾回收的成本卻很高,因此執(zhí)行地不那么頻繁。
通常只有20%左右的新生代會晉升為老生代。老生代空間只有在快被耗盡的時候,才會執(zhí)行垃圾回收。V8引擎采用了兩種回收算法來實現(xiàn):Scavenge 和 Mark-Sweep 。
Scavenge回收算法運算速度很快,用于新生代;慢一些的Mark-Sweep算法用于老生代。
四、現(xiàn)實案例The Meteor Case-Study2013年,Meteor的作者們發(fā)布了一個他們遇到的內(nèi)存泄露的例子。出問題的代碼段如下:
var theThing = null var replaceThing = function () { var originalThing = theThing var unused = function () { if (originalThing) console.log("hi") } theThing = { longStr: new Array(1000000).join("*"), someMethod: function () { console.log(someMessage) } }; }; setInterval(replaceThing, 1000)
更多相關(guān)閱讀Well, the typical way that closures are implemented is that every
function object has a link to a dictionary-style object representing
its lexical scope. If both functions defined inside replaceThing
actually used originalThing, it would be important that they both get
the same object, even if originalThing gets assigned to over and over,
so both functions share the same lexical environment. Now, Chrome"s V8
JavaScript engine is apparently smart enough to keep variables out of
the lexical environment if they aren"t used by any closures - from the
Meteor blog.通常來說,實現(xiàn)閉包的方式為:每個函數(shù)對象都鏈接到一個字典式的對象,此對象表現(xiàn)其詞法作用域。如果replaceThing中兩個函數(shù)都使用了變量originalThing,那么即便originalThing被多次賦值,也必須保證這兩個函數(shù)得到的永遠(yuǎn)是同一個對象,才能保證兩個函數(shù)共享一個詞法作用域。那么問題來了,Chrome的V8
JavaScript引擎只有在一個變量沒有被用在任何閉包中的時候,才會將其隔離在詞法環(huán)境之外。 - Meteor blog.
Finding a memory leak in Node.js
JavaScript Garbage Collection Improvements - Orinoco
memorymanagement.org
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/91262.html
摘要:原文出處垃圾回收機制標(biāo)記清除算法介紹最主要的理論算法之一,在實踐過程中,為了真實情景需要,需要許多調(diào)整。因此不會僅僅標(biāo)記清除,垃圾回收期間,內(nèi)存整理進程同時在工作。不同內(nèi)存區(qū)域的垃圾收集機制不辣么容易理解。 原文出處:java垃圾回收機制 標(biāo)記清除算法介紹最主要的理論算法之一,在實踐過程中,為了真實情景需要,需要許多調(diào)整。舉一個簡單例子,我們檢查JVM需要做的各種事情,以便我們安全地去...
摘要:垃圾回收器追蹤所有正在使用的對象,將無用對象標(biāo)記為垃圾。自動化指針內(nèi)存回收自動化的最好方式之一是使用鉤子函數(shù)。它們可能因為多種原因發(fā)生,但是這種垃圾回收器是最主流的一種。 原文出處:What Is Garbage Collection? 一眼就應(yīng)該從名稱看出垃圾回收機制的含義-查找垃圾,然后丟棄。事實正好相反。垃圾回收器追蹤所有正在使用的對象,將無用對象標(biāo)記為垃圾。請留意,我們開始研究...
摘要:關(guān)鍵是釋放內(nèi)存這一步,各種語言都有自己的垃圾回收簡稱機制。用的是這種,在字末位進行標(biāo)識,為指針。對于而言,最初的垃圾回收機制,是基于引用計次來做的。老生代的垃圾回收,分兩個階段標(biāo)記清理有和這兩種方式。 不管是高級語言,還是低級語言。內(nèi)存的管理都是: 分配內(nèi)存 使用內(nèi)存(讀或?qū)懀?釋放內(nèi)存 前兩步,大家都沒有太大異議。關(guān)鍵是釋放內(nèi)存這一步,各種語言都有自己的垃圾回收(garbage ...
摘要:它將堆內(nèi)存一分為二每一部分空間稱為。以的垃圾回收堆內(nèi)存為例做一次小的垃圾回收需要毫秒以上做一次非增量式的垃圾回收甚至要秒以上。這是垃圾回收中引起線程暫停執(zhí)行的時間在這樣的時間花銷下應(yīng)用的性能和響應(yīng)能力都會直線下降。 我們通常理解的 javascript 垃圾回收機制都停留在表面,會釋放不被引用變量內(nèi)存,最近在讀《深入淺出node.js》的書,詳細(xì)了解了下 v8 垃圾回收的算法,記錄了一...
摘要:的內(nèi)存限制和垃圾回收機制內(nèi)存限制內(nèi)存限制一般的后端語言開發(fā)中,在基本的內(nèi)存使用是沒有限制的。的內(nèi)存分代目前沒有一種垃圾自動回收算法適用于所有場景,所以的內(nèi)部采用的其實是兩種垃圾回收算法。 前言 從前端思維轉(zhuǎn)變到后端, 有一個很重要的點就是內(nèi)存管理。以前寫前端因為只是在瀏覽器上運行, 所以對于內(nèi)存管理一般不怎么需要上心, 但是在服務(wù)器端, 則需要斤斤計較內(nèi)存。 V8的內(nèi)存限制和垃圾回收機...
閱讀 2216·2021-09-07 09:58
閱讀 3391·2019-08-30 14:07
閱讀 1305·2019-08-29 12:32
閱讀 667·2019-08-29 11:06
閱讀 3692·2019-08-26 18:18
閱讀 3731·2019-08-26 17:35
閱讀 1381·2019-08-26 11:35
閱讀 611·2019-08-26 11:35