摘要:基本概念垃圾回收機(jī)制。對(duì)兩個(gè)不同生代的不同垃圾回收策略構(gòu)成了整個(gè)的垃圾回收機(jī)制。此種方式會(huì)導(dǎo)致下一次內(nèi)存中產(chǎn)生大量碎片,即內(nèi)存空間不連續(xù),導(dǎo)致內(nèi)存分配時(shí)面對(duì)大對(duì)象可能會(huì)無(wú)法滿足,提前出發(fā)下一次的垃圾回收機(jī)制。
: 聊一聊垃圾回收機(jī)制吧。基本概念
: 恩,垃圾回收是自動(dòng)的。
GC(Garbage collection)垃圾回收機(jī)制。目的是解釋器去判別需要回收的內(nèi)容,當(dāng)解釋器認(rèn)為一個(gè)占著房子的人已經(jīng)沒(méi)有存在的意義了,就自動(dòng)收回房子重新對(duì)外出租(available)。JS和PY都選擇不相信程序員,選擇自己操控內(nèi)存問(wèn)題。
Node的對(duì)象都是分配在堆內(nèi)存上,V8主要把內(nèi)存分為 new-space 和 old-space ,64位系統(tǒng)對(duì)應(yīng)的大小約為 32MB 和 1400MB(32位系統(tǒng)對(duì)應(yīng)折半)。二者共同構(gòu)成Node的總內(nèi)存(約為1.4G)。
新生代空間的對(duì)象生存周期比較短,容量也比較小,老生代的對(duì)象都是“強(qiáng)硬派”,生命力旺盛,容量比較大。Node 不是 HipHop 為啥非要把內(nèi)存分這個(gè) “new-school”,“old-school” ?,就是因?yàn)樵趯?shí)際的情況中,各種垃圾回收策略并不能滿足解決不同的對(duì)象聲明周期長(zhǎng)短不一的問(wèn)題,而只是針對(duì)某一種特定情況非常有用,所以基于分代策略能夠根據(jù)對(duì)象的生命周期不同,采用最適合的算法策略進(jìn)行高效垃圾回收。
Node對(duì)兩個(gè)不同生代的不同垃圾回收策略構(gòu)成了整個(gè)Node的垃圾回收機(jī)制。下面就來(lái)詳細(xì)說(shuō)明這兩個(gè)不同的生代究竟是怎么處理的辣雞的。
new-space 與 Scavenge算法回顧一下 new-space 的特點(diǎn):對(duì)象的生存周期普遍都比較短。這意味著,“頑固派”對(duì)象比較少
Scavenge 策略把 new-space 一分為兩個(gè) “simispace"(半空間),一個(gè)叫 處于使用狀態(tài)的 From 空間 一個(gè)叫閑置的 TO 空間。整個(gè)回收的過(guò)程就是如下圖:
引用計(jì)數(shù)與閉包那么在新生代中如何讓GC知道某一個(gè)對(duì)象已經(jīng)沒(méi)有價(jià)值即該對(duì)象的生命周期已經(jīng)結(jié)束了呢?
引用計(jì)數(shù):所謂引用計(jì)數(shù)就是跟蹤并記錄每一個(gè)值被引用的次數(shù),當(dāng)我們生命了一個(gè)變量并且將一個(gè)引用類型賦值給該變量,那么該引用對(duì)象的引用計(jì)數(shù)加一,如果同一個(gè)變量又賦值給了另外一個(gè)變量,那么計(jì)數(shù)再一次增加1。那么相反的是如果某一個(gè)有引用類型值得變量又被賦了另外一個(gè)值,那么原先的引用類型的計(jì)數(shù)就相應(yīng)的減一,或者當(dāng)在一個(gè)函數(shù)執(zhí)行完畢之后,該函數(shù)在執(zhí)行時(shí)所創(chuàng)建的作用域?qū)N毀,與此同時(shí)在該函數(shù)作用域中聲明的局部變量所對(duì)應(yīng)的內(nèi)存空間的引用計(jì)數(shù)將隨之減一,不出現(xiàn)閉包的情況下,下一次的垃圾回收機(jī)制在被觸發(fā)的時(shí)候,作用域中的變量所對(duì)應(yīng)的空間就會(huì)結(jié)束聲明周期。像下面的代碼那樣:
function callOnce(){ let local = {} let foo = {} let bar = {a:{},b:{}} }
那么所謂閉包,一個(gè)在面試中都快被問(wèn)爛了的概念:),其實(shí)說(shuō)白了就是運(yùn)用函數(shù)可以作為參數(shù)或者返回值使得一個(gè)外部作用域想要訪問(wèn)內(nèi)部作用域中的私有變量的一種方式
function foo(){ let local = {a:"ray"} return function(){ return local } } let bar = foo()
上述代碼就形成了一個(gè)閉包,使得一旦有了變量引用了foo函數(shù)的返回值函數(shù),就使得該返回值函數(shù)得不到釋放,也使得foo函數(shù)的作用域得不到釋放,即內(nèi)存也不會(huì)釋放,除非不再有引用,才會(huì)逐步釋放。
old-space 與 標(biāo)記-清除/標(biāo)記-整理分代之中除了 new-space 之外即是 old-space 了 ,分代的目的是為了針對(duì)不同的對(duì)象生命周期運(yùn)用不同的回收算法。
滿足條件晉升到老生代的的對(duì)象都有著比較頑強(qiáng)的生機(jī),意味著在老生代中,存活的對(duì)象占有者很大的比重,使用新生代基于復(fù)制的策略會(huì)有著比較差的效率,此外,新生代中一分為二的空間策略面對(duì)著存活對(duì)象較多的情況也比較不合適。所以在老生代中V8采用了標(biāo)記-清除與標(biāo)記-整理這這兩種方式結(jié)合的策略。
標(biāo)記清除分為標(biāo)記和清除兩個(gè)步驟,先在老生代中遍歷所有的對(duì)象,把那些在遍歷過(guò)程中還活著的對(duì)象都加上一個(gè)標(biāo)記,在下一步的時(shí)候那些沒(méi)有被標(biāo)記的對(duì)象就會(huì)自然的被回收了。示意圖如下:
黑色的即為沒(méi)有被標(biāo)記已經(jīng)死了對(duì)象,下一次就會(huì)被回收內(nèi)存空間。
此種方式會(huì)導(dǎo)致下一次內(nèi)存中產(chǎn)生大量碎片,即內(nèi)存空間不連續(xù),導(dǎo)致內(nèi)存分配時(shí)面對(duì)大對(duì)象可能會(huì)無(wú)法滿足,提前出發(fā)下一次的垃圾回收機(jī)制。所以便又有了一種標(biāo)記-整理的方式。
對(duì)比標(biāo)記-清除,他多了異步整理的過(guò)程,即把標(biāo)記為存活的兌現(xiàn)統(tǒng)統(tǒng)整理到內(nèi)存的一端,完成整理之后直接清除掉另一端連續(xù)的死亡對(duì)象空間,如下:
最后,由于標(biāo)記-整理這種方式設(shè)計(jì)大量移動(dòng)對(duì)象操作,導(dǎo)致速度非常慢,多以 V8 主要使用標(biāo)記-清除的方式,當(dāng)老生代空間中不足以為新生代晉升過(guò)來(lái)的頑固派們分配空間的時(shí)候,才使用標(biāo)記-整理
V8的優(yōu)化由于在進(jìn)行垃圾回收的時(shí)候會(huì)導(dǎo)致應(yīng)用邏輯陷入全停頓的狀態(tài),在進(jìn)行老生代的回收時(shí),V8引入了 增量式標(biāo)記,增量式整理,延遲清理等策略,中心思想就是為了能讓一次垃圾回收過(guò)程不那么占用太長(zhǎng)的應(yīng)用程序停頓時(shí)間,而提出類似于時(shí)間片輪轉(zhuǎn)一樣的策略,讓整個(gè)過(guò)程“雨露均沾”,GC弄一會(huì),應(yīng)用程序執(zhí)行一會(huì)。
堆內(nèi)內(nèi)存與堆外內(nèi)存使用process.memoryUsage()可以查看node進(jìn)程的內(nèi)存使用情況。單位是字節(jié)
{ rss: 22233088, heapTotal: 7708672, heapUsed: 5095384, external: 28898 }
其中 rss 就是 node 進(jìn)程的常駐內(nèi)存。V8對(duì)內(nèi)存有限制,但是不同于瀏覽器,Node在服務(wù)端難免會(huì)操作大文件流,所以有了一種跳脫 V8 的內(nèi)存限制方式就是使用 buffer 進(jìn)行堆外內(nèi)存分配。如下代碼:
let showMem = () => { let mem = process.memoryUsage() //process.memoryUsage()值得單位都是字節(jié),轉(zhuǎn)化為兆 let format = (byte) => { return (byte/1024/1024).toFixed(2)+"MB" } console.log(`rss:${format(mem.rss)} heapTotal:${format(mem.heapTotal)} heapUsed:${format(mem.heapUsed)} external:${format(mem.external)}`); console.log("------------------------------------"); } let useMem = () => { let size = 20*1024*1024 let arr = new Array(size) for (let index = 0; index < size; index++) { arr[index] = 0 } return arr } let useMemBuffer = () => { let size = 20*1024*1024 let buf = new Buffer(size) for (let index = 0; index < size; index++) { buf[index] = 0 } return buf } let total = [] for (let index = 0; index < 100; index++) { showMem() total.push(useMemBuffer()) } showMem()
下面為分別調(diào)用 useMem()和useMemBuffer() 使用數(shù)組是通過(guò)V8分配堆內(nèi)存,使用 Buffer 是不使用V8分配堆外內(nèi)存,分別打印:
上圖一表示堆內(nèi)內(nèi)存在一定循環(huán)次數(shù)之后達(dá)到溢出邊緣,
圖二可見(jiàn),external和rss在不斷增大但是其值早就突破了V8的內(nèi)存上限。是因?yàn)?strong>堆外內(nèi)存并不是V8進(jìn)行內(nèi)存分配的。
下一篇所要討論的緩存算法中,緩存就是一個(gè)有可能造成內(nèi)存泄漏的場(chǎng)景。
參考:
《深入淺出NodeJS》-- 樸靈
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/93505.html
摘要:垃圾回收內(nèi)存管理實(shí)踐先通過(guò)一個(gè)來(lái)看看在中進(jìn)行垃圾回收的過(guò)程是怎樣的內(nèi)存泄漏識(shí)別在環(huán)境里提供了方法用來(lái)查看當(dāng)前進(jìn)程內(nèi)存使用情況,單位為字節(jié)中保存的進(jìn)程占用的內(nèi)存部分,包括代碼本身?xiàng)6选? showImg(https://segmentfault.com/img/remote/1460000019894672?w=640&h=426);作者 | 五月君Node.js 技術(shù)棧 | https:...
摘要:的內(nèi)存限制和垃圾回收機(jī)制內(nèi)存限制內(nèi)存限制一般的后端語(yǔ)言開發(fā)中,在基本的內(nèi)存使用是沒(méi)有限制的。的內(nèi)存分代目前沒(méi)有一種垃圾自動(dòng)回收算法適用于所有場(chǎng)景,所以的內(nèi)部采用的其實(shí)是兩種垃圾回收算法。 前言 從前端思維轉(zhuǎn)變到后端, 有一個(gè)很重要的點(diǎn)就是內(nèi)存管理。以前寫前端因?yàn)橹皇窃跒g覽器上運(yùn)行, 所以對(duì)于內(nèi)存管理一般不怎么需要上心, 但是在服務(wù)器端, 則需要斤斤計(jì)較內(nèi)存。 V8的內(nèi)存限制和垃圾回收機(jī)...
摘要:一前言的垃圾回收機(jī)制使用垃圾回收機(jī)制來(lái)自動(dòng)管理內(nèi)存。垃圾回收器只會(huì)針對(duì)新生代內(nèi)存區(qū)老生代指針區(qū)以及老生代數(shù)據(jù)區(qū)進(jìn)行垃圾回收。分別對(duì)新生代和老生代使用不同的垃圾回收算法來(lái)提升垃圾回收的效率。 V8 實(shí)現(xiàn)了準(zhǔn)確式 GC,GC 算法采用了分代式垃圾回收機(jī)制。因此,V8 將內(nèi)存(堆)分為新生代和老生代兩部分。 一、前言 V8的垃圾回收機(jī)制:JavaScript使用垃圾回收機(jī)制來(lái)自動(dòng)管理內(nèi)存。垃...
摘要:新生代的對(duì)象為存活時(shí)間較短的對(duì)象,老生代中的對(duì)象為存活時(shí)間較長(zhǎng)或常駐內(nèi)存的對(duì)象。分別對(duì)新生代和老生代使用不同的垃圾回收算法來(lái)提升垃圾回收的效率。如果指向老生代我們就不必考慮它了。 這篇文章的所有內(nèi)容均來(lái)自 樸靈的《深入淺出Node.js》及A tour of V8:Garbage Collection,后者還有中文翻譯版V8 之旅: 垃圾回收器,我在這里只是做了個(gè)記錄和結(jié)合 垃圾回收...
摘要:關(guān)鍵是釋放內(nèi)存這一步,各種語(yǔ)言都有自己的垃圾回收簡(jiǎn)稱機(jī)制。用的是這種,在字末位進(jìn)行標(biāo)識(shí),為指針。對(duì)于而言,最初的垃圾回收機(jī)制,是基于引用計(jì)次來(lái)做的。老生代的垃圾回收,分兩個(gè)階段標(biāo)記清理有和這兩種方式。 不管是高級(jí)語(yǔ)言,還是低級(jí)語(yǔ)言。內(nèi)存的管理都是: 分配內(nèi)存 使用內(nèi)存(讀或?qū)懀?釋放內(nèi)存 前兩步,大家都沒(méi)有太大異議。關(guān)鍵是釋放內(nèi)存這一步,各種語(yǔ)言都有自己的垃圾回收(garbage ...
閱讀 2971·2021-11-08 13:20
閱讀 1038·2021-09-22 15:20
閱讀 668·2019-08-30 15:53
閱讀 1974·2019-08-30 15:43
閱讀 1287·2019-08-29 17:21
閱讀 545·2019-08-29 12:15
閱讀 2384·2019-08-28 17:51
閱讀 3151·2019-08-26 13:26