基本概念javascript
V8是Node的JavaScript執行引擎,V8引擎實際是一個高性能虛擬機。Node在JavaScript的執行直接受益於V8,能夠隨着V8的升級就能享受更好的性能或新的語言特性(如ES5和ES6)java
兩者之間的關係node
1)大小限制說明程序員
對於通常的後端開發語言,基本內存使用是沒有限制的,可是在Node中經過javaScript使用內存時只能使用部份內存(64位系統下約爲1.4G,32位系統下約爲0.6G)後端
2)限制的緣由與特殊說明微信
Node基於V8構建,因此在Node中使用javaScript基本都是經過V8本身的方式進行分配和管理的。可是Node的內存並不徹底是經過V8進行分配管理的。查看內存使用狀況的時候,發現堆中的內存用量老是小於進程的常駐內存用量rss。Node中的內存使用並不是都是經過V8進行分配的,還有一些不是經過V8進行分配的對象,咱們稱之爲堆外內存,堆外內存文章末尾會有一個說明(例如Buffer對象就不一樣於其餘對象,他不通過V8的內存分配機制,不會有堆內存的限制)閉包
3)V8的對象分配函數
V8中,全部的javaScript對象都是經過堆來進行分配的。V8的堆內存包括heapToal(已經申請到的堆內存),heapUsed(當前使用的堆內存);咱們在代碼中聲明變量並賦值的時候,所使用的對象的內存就分配在堆中。若是已申請的堆空閒內存不夠分配新的對象,將繼續申請堆內存,直到堆的大小超過V8的限制爲止。性能
說明:基於V8這種限制將會致使Node沒法操做大內存對象,也所以後來出現了buffer這種不受V8丟內存控制的堆外內存管理。優化
因爲V8已經對內存作了限制,咱們應該作到高效的使用內存,讓垃圾回收機制更高效的工做,避免一些不容易回收內存的出現。
做用域
在JavaScript中,能造成做用域的有函數,with以及全局做用域。
1)做用域舉例最基本的內存回收過程
var a=function(){
var local={};
}
複製代碼
函數a在每次被調用的時候會建立對應的做用域,函數執行結束後,該做用域將會銷燬。同時由於該做用域中聲明的局部變量分配在該做用域上,隨做用域的銷燬而銷燬。只被局部變量引用的對象存活週期較短。代碼中,因爲對象較小,將會分配在新生代的Form空間中。做用域失效後,局部變量local失效,其引用的對象將會在下次垃圾回收時被釋放。
2)做用域中的變量查找
JavaScript在執行時會查找變量定義在哪,最早查找的當前做用域,當前做用域沒有,會向上級的做用域查找,直到最頂層全局做用域查到,若是沒有最後返回undefine。
3)變量的主動釋放
若是變量是全局變量(經過var聲明或定義在global變量上),全局做用域直到進程退出才能釋放,這種狀況將致使引用的對象常駐內存(常駐在老生代中)。這種須要釋放常駐內存中的對象,可使用delete操做來刪除引用關係,或者將變量從新賦值,讓舊對象脫離引用關係(也就是對象的引用即所佔的內存空間本來指向某個變量如今指向空獲未定義),這樣在接下來的老生代內存 清 除和整理的過程當中會被釋放。
global.foo="i am gang"; console.log(global.foo);// i am gangdelete global.foo;
複製代碼//或者從新賦值 global.foo=undefined;// or null console.log(global.foo);//undefined 複製代碼
說明:雖然兩種方式均可以主動釋放變量引用的對象(也就是那一小塊內存),可是推薦你們使用從新賦值的方法,由於在V8中經過delete刪除對象的屬性有可能干擾V8的優化。
閉包
在javaScript中,實現外部做用域訪問內部做用域中變量的方法叫作閉包(closure)。這得益於高階函數的特性:函數能夠做爲參數或者返回值。 閉包它實現了外部做用域訪問內部做用域中變量的方法。這句話須要好好理解。
簡單例子說明閉包 兩段代碼對比:
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()); //局部變量 }; 複製代碼
分析第二段代碼,函數C執行完成後,局部變量local會隨着做用域的銷燬而被回收。可是注意這裏的特色是返回值是一個匿名函數,並且這個函數中具有了訪問local的條件,後面的代碼執行,外部做用域是沒法直接訪問local的,可是若要訪問它,只要經過這個中間函數稍做週轉便可。以上就是閉包的基本分析,如今可以更好的理解我畫重點的那句話了吧。
對於閉包的詳細介紹,你們能夠看個人這篇博客javascript中的閉包這一篇就夠了
$ node
> process.memoryUsage();
{
rss:14958592,
heapTotal:7195904,
heapUsed:2821496
}
複製代碼
heapTotal:V8中已申請的堆內存
heapUsed:V8中當前使用的堆內存
rss:進程的常駐內存部分
$ node
> os.totalmem()
82132131
> os.freemem()
31273127
複製代碼
os.totalmem 操做系統的總內存
os.freemem 操做系統的閒置內存
堆外內存
查看v8內存使用狀況,process.memoryUsage()的結果能夠看到,V8堆中的內存用量老是小於進程的常駐內存用量rss,也就是說Node中的內存使用並不是都是V8控制,還有一部分不是經過V8分配的(rss-heaptotal這部分),不經過V8分配的內存稱之爲堆外內存。
使用buffer每次構造200MB的內存,代碼以下:
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;
};
複製代碼
代碼執行過程當中,查看內存使用狀況會發現到最後,V8的使用內存heapUsed和申請的內存heaptotal基本不變,而常駐內存rss在不斷增長,能夠看出buffer對象不一樣於其它對象,不通過V8內存分配機制,不會有堆內存的限制。後面的文章會對buffer進行詳細的講解。
Node對內存泄漏十分敏感,一旦線上應用有成千上萬的流量,哪怕一個字節的內存泄漏也會形成堆積,垃圾回收過程當中將會耗費更多時間進行對象掃描,應用響應緩慢,直到進程內存溢出,應用奔潰。
內存泄漏的本質
應當回收的對象出現意外而沒有被回收,變成常駐在老生代中的對象。
形成內存泄漏的緣由
1)做用域未釋放
2)隊列消費不及時
3)做用域未釋放
內存泄漏這裏有一個點,咱們每次把服務重啓的時候會不會這個內存泄漏的堆積從新開始計算,forever restart和forever stop以後在forever start
歡迎你們關注個人公衆號——程序員成長指北。請自行微信搜索——「程序員成長指北」