Node.js內(nèi)存管理基礎(chǔ)講解

Node與V8

  • 基本概念

    V8是Node的JavaScript執(zhí)行引擎,V8引擎實(shí)際是一個(gè)高性能虛擬機(jī)倦挂。Node在JavaScript的執(zhí)行直接受益于V8引瀑,可以隨著V8的升級(jí)就能享受更好的性能或新的語(yǔ)言特性(如ES5和ES6)

  • 二者之間的關(guān)系

    1)大小限制說(shuō)明

    對(duì)于一般的后端開發(fā)語(yǔ)言驼鹅,基本內(nèi)存使用是沒有限制的,但是在Node中通過javaScript使用內(nèi)存時(shí)只能使用部分內(nèi)存(64位系統(tǒng)下約為1.4G纽门,32位系統(tǒng)下約為0.6G)

    2)限制的原因與特殊說(shuō)明

    Node基于V8構(gòu)建重绷,所以在Node中使用javaScript基本都是通過V8自己的方式進(jìn)行分配和管理的。但是Node的內(nèi)存并不完全是通過V8進(jìn)行分配管理的膜毁。查看內(nèi)存使用情況的時(shí)候昭卓,發(fā)現(xiàn)堆中的內(nèi)存用量總是小于進(jìn)程的常駐內(nèi)存用量rss。Node中的內(nèi)存使用并非都是通過V8進(jìn)行分配的瘟滨,還有一些不是通過V8進(jìn)行分配的對(duì)象候醒,我們稱之為堆外內(nèi)存,堆外內(nèi)存文章末尾會(huì)有一個(gè)說(shuō)明(例如Buffer對(duì)象就不同于其他對(duì)象杂瘸,他不經(jīng)過V8的內(nèi)存分配機(jī)制倒淫,不會(huì)有堆內(nèi)存的限制)

3)V8的對(duì)象分配

V8中,所有的javaScript對(duì)象都是通過堆來(lái)進(jìn)行分配的败玉。V8的堆內(nèi)存包括heapToal(已經(jīng)申請(qǐng)到的堆內(nèi)存)敌土,heapUsed(當(dāng)前使用的堆內(nèi)存);我們?cè)诖a中聲明變量并賦值的時(shí)候运翼,所使用的對(duì)象的內(nèi)存就分配在堆中返干。如果已申請(qǐng)的堆空閑內(nèi)存不夠分配新的對(duì)象,將繼續(xù)申請(qǐng)堆內(nèi)存血淌,直到堆的大小超過V8的限制為止矩欠。

說(shuō)明:基于V8這種限制將會(huì)導(dǎo)致Node無(wú)法操作大內(nèi)存對(duì)象财剖,也因此后來(lái)出現(xiàn)了buffer這種不受V8丟內(nèi)存控制的堆外內(nèi)存管理。

開發(fā)過程中的那些不好回收的內(nèi)存(高效使用內(nèi)存)

由于V8已經(jīng)對(duì)內(nèi)存做了限制癌淮,我們應(yīng)該做到高效的使用內(nèi)存躺坟,讓垃圾回收機(jī)制更高效的工作,避免一些不容易回收內(nèi)存的出現(xiàn)乳蓄。

  • 作用域

    在JavaScript中咪橙,能形成作用域的有函數(shù),with以及全局作用域虚倒。

    1)作用域舉例最基本的內(nèi)存回收過程

var a=function(){
    var local={};
}

函數(shù)a在每次被調(diào)用的時(shí)候會(huì)創(chuàng)建對(duì)應(yīng)的作用域,函數(shù)執(zhí)行結(jié)束后裹刮,該作用域?qū)?huì)銷毀庞瘸。同時(shí)因?yàn)樵撟饔糜蛑新暶鞯木植孔兞糠峙湓谠撟饔糜蛏希S作用域的銷毀而銷毀擦囊。只被局部變量引用的對(duì)象存活周期較短。代碼中瞬场,由于對(duì)象較小,將會(huì)分配在新生代的Form空間中贯被。作用域失效后眼五,局部變量local失效看幼,其引用的對(duì)象將會(huì)在下次垃圾回收時(shí)被釋放。

2)作用域中的變量查找

JavaScript在執(zhí)行時(shí)會(huì)查找變量定義在哪幌陕,最先查找的當(dāng)前作用域诵姜,當(dāng)前作用域沒有,會(huì)向上級(jí)的作用域查找搏熄,直到最頂層全局作用域查到棚唆,如果沒有最后返回undefine。

3)變量的主動(dòng)釋放

如果變量是全局變量(通過var聲明或定義在global變量上)心例,全局作用域直到進(jìn)程退出才能釋放宵凌,這種情況將導(dǎo)致引用的對(duì)象常駐內(nèi)存(常駐在老生代中)。這種需要釋放常駐內(nèi)存中的對(duì)象止后,可以使用delete操作來(lái)刪除引用關(guān)系摆寄,或者將變量重新賦值,讓舊對(duì)象脫離引用關(guān)系(也就是對(duì)象的引用即所占的內(nèi)存空間原本指向某個(gè)變量現(xiàn)在指向空獲未定義),這樣在接下來(lái)的老生代內(nèi)存 清 除和整理的過程中會(huì)被釋放微饥。

global.foo="i am gang";
console.log(global.foo);// i am gang

delete global.foo;

//或者重新賦值
global.foo=undefined;// or null
console.log(global.foo);//undefined

說(shuō)明:雖然兩種方式都可以主動(dòng)釋放變量引用的對(duì)象(也就是那一小塊內(nèi)存)逗扒,但是推薦大家使用重新賦值的方法,因?yàn)樵赩8中通過delete刪除對(duì)象的屬性有可能干擾V8的優(yōu)化欠橘。

  • 閉包

    在javaScript中矩肩,實(shí)現(xiàn)外部作用域訪問內(nèi)部作用域中變量的方法叫做閉包(closure)。這得益于高階函數(shù)的特性:函數(shù)可以作為參數(shù)或者返回值肃续。 閉包它實(shí)現(xiàn)了外部作用域訪問內(nèi)部作用域中變量的方法幕帆。這句話需要好好理解。

    簡(jiǎn)單例子說(shuō)明閉包 兩段代碼對(duì)比:

var A=function(){
    (function(){
        var local="局部變量";
    }());
    console.log(local); //local未定義異常
}

var B=function(){
    var C=function(){
        var local="局部變量";
        return function(){
            return local;
        };
    };
    var c=C();
    console.log(c()); //局部變量
};

分析第二段代碼荠列,函數(shù)C執(zhí)行完成后撒遣,局部變量local會(huì)隨著作用域的銷毀而被回收。但是注意這里的特點(diǎn)是返回值是一個(gè)匿名函數(shù)瞧捌,而且這個(gè)函數(shù)中具備了訪問local的條件棵里,后面的代碼執(zhí)行,外部作用域是無(wú)法直接訪問local的姐呐,但是若要訪問它殿怜,只要通過這個(gè)中間函數(shù)稍作周轉(zhuǎn)即可。以上就是閉包的基本分析曙砂,現(xiàn)在能夠更好的理解我畫重點(diǎn)的那句話了吧头谜。

對(duì)于閉包的詳細(xì)介紹,大家可以看我的這篇博客javascript中的閉包這一篇就夠了

內(nèi)存相關(guān)基本命令使用

  • V8中內(nèi)存使用情況查看:
$ node
> process.memoryUsage();
{
    rss:14958592,
    heapTotal:7195904,
    heapUsed:2821496
}

heapTotal:V8中已申請(qǐng)的堆內(nèi)存

heapUsed:V8中當(dāng)前使用的堆內(nèi)存

rss:進(jìn)程的常駐內(nèi)存部分

  • 查看系統(tǒng)的內(nèi)存占用
$ node
> os.totalmem()
82132131
> os.freemem()
31273127

os.totalmem 操作系統(tǒng)的總內(nèi)存

os.freemem 操作系統(tǒng)的閑置內(nèi)存

  • 堆外內(nèi)存

    查看v8內(nèi)存使用情況鸠澈,process.memoryUsage()的結(jié)果可以看到柱告,V8堆中的內(nèi)存用量總是小于進(jìn)程的常駐內(nèi)存用量rss,也就是說(shuō)Node中的內(nèi)存使用并非都是V8控制笑陈,還有一部分不是通過V8分配的(rss-heaptotal這部分)际度,不通過V8分配的內(nèi)存稱之為堆外內(nèi)存。

    使用buffer每次構(gòu)造200MB的內(nèi)存新锈,代碼如下:

var useMem=function(){
    var size=200*1024*1024;
    var buffer=new Buffer(size);
    for(var i=0;i<size;i++){
        buffer[i]=0
    }
    return buffer;
};

代碼執(zhí)行過程中,查看內(nèi)存使用情況會(huì)發(fā)現(xiàn)到最后妹笆,V8的使用內(nèi)存heapUsed和申請(qǐng)的內(nèi)存heaptotal基本不變拳缠,而常駐內(nèi)存rss在不斷增加,可以看出buffer對(duì)象不同于其它對(duì)象海渊,不經(jīng)過V8內(nèi)存分配機(jī)制,不會(huì)有堆內(nèi)存的限制盔憨。后面的文章會(huì)對(duì)buffer進(jìn)行詳細(xì)的講解郁岩。

內(nèi)存泄露

Node對(duì)內(nèi)存泄漏十分敏感缺狠,一旦線上應(yīng)用有成千上萬(wàn)的流量,哪怕一個(gè)字節(jié)的內(nèi)存泄漏也會(huì)造成堆積如叼,垃圾回收過程中將會(huì)耗費(fèi)更多時(shí)間進(jìn)行對(duì)象掃描笼恰,應(yīng)用響應(yīng)緩慢囚衔,直到進(jìn)程內(nèi)存溢出雕沿,應(yīng)用奔潰审轮。

  • 內(nèi)存泄漏的本質(zhì)

    應(yīng)當(dāng)回收的對(duì)象出現(xiàn)意外而沒有被回收,變成常駐在老生代中的對(duì)象疾渣。

  • 造成內(nèi)存泄漏的原因

    1)作用域未釋放

    2)隊(duì)列消費(fèi)不及時(shí)

    3)作用域未釋放

內(nèi)存泄漏這里有一個(gè)點(diǎn)榴捡,我們每次把服務(wù)重啟的時(shí)候會(huì)不會(huì)這個(gè)內(nèi)存泄漏的堆積重新開始計(jì)算吊圾,forever restart和forever stop之后在forever start

覺得本文對(duì)你有幫助?請(qǐng)分享給更多人

我的公眾號(hào).jpg

歡迎大家關(guān)注我的公眾號(hào)——程序員成長(zhǎng)指北啰劲。請(qǐng)自行微信搜索——“程序員成長(zhǎng)指北”

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末蝇裤,一起剝皮案震驚了整個(gè)濱河市栓辜,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌藕甩,老刑警劉巖辛萍,帶你破解...
    沈念sama閱讀 222,104評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件悯许,死亡現(xiàn)場(chǎng)離奇詭異辉阶,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)垃僚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門谆棺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)罕袋,“玉大人浴讯,你說(shuō)我怎么就攤上這事⊙霾” “怎么了饥侵?”我有些...
    開封第一講書人閱讀 168,697評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵爆捞,是天一觀的道長(zhǎng)勾拉。 經(jīng)常有香客問我,道長(zhǎng)成肘,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,836評(píng)論 1 298
  • 正文 為了忘掉前任砚偶,我火速辦了婚禮染坯,結(jié)果婚禮上丘逸,老公的妹妹穿的比我還像新娘。我一直安慰自己仲锄,他們只是感情好湃鹊,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,851評(píng)論 6 397
  • 文/花漫 我一把揭開白布币呵。 她就那樣靜靜地躺著富雅,像睡著了一般肛搬。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蛤奢,一...
    開封第一講書人閱讀 52,441評(píng)論 1 310
  • 那天,我揣著相機(jī)與錄音拜秧,去河邊找鬼。 笑死志衍,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的培廓。 我是一名探鬼主播春叫,決...
    沈念sama閱讀 40,992評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼价匠,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼霞怀!你這毒婦竟也來(lái)了毙石?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,899評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎滤灯,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體鳞骤,經(jīng)...
    沈念sama閱讀 46,457評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,529評(píng)論 3 341
  • 正文 我和宋清朗相戀三年顷帖,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了贬墩。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,664評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖唠粥,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情厅贪,我是刑警寧澤养涮,帶...
    沈念sama閱讀 36,346評(píng)論 5 350
  • 正文 年R本政府宣布懈凹,位于F島的核電站,受9級(jí)特大地震影響介评,放射性物質(zhì)發(fā)生泄漏们陆。R本人自食惡果不足惜情屹,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,025評(píng)論 3 334
  • 文/蒙蒙 一坪仇、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧垃你,春花似錦椅文、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至凌摄,卻和暖如春羡蛾,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背望伦。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工林说, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人屯伞。 一個(gè)月前我還...
    沈念sama閱讀 49,081評(píng)論 3 377
  • 正文 我出身青樓劣摇,卻偏偏與公主長(zhǎng)得像钧惧,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子乾颁,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,675評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容