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)分享給更多人
歡迎大家關(guān)注我的公眾號(hào)——程序員成長(zhǎng)指北啰劲。請(qǐng)自行微信搜索——“程序員成長(zhǎng)指北”