摘要:進(jìn)程運(yùn)行中沒被回收肯定屬于內(nèi)存泄漏關(guān)于這個(gè)定義引起討論是在進(jìn)程退出或崩潰后的情況。關(guān)于異步處理問題不適合處理復(fù)雜的狀態(tài)機(jī)解決方案是使用隊(duì)列結(jié)構(gòu)進(jìn)行可控的異步并發(fā)。
front to back
業(yè)務(wù)上 : front 面向展示 交互 ; back 功能 服務(wù) 數(shù)據(jù)一致性等等
環(huán)境 : front browser webview 單機(jī); back 集群 高并發(fā)
思想差異 : front 快速開發(fā) 快速渲染 視覺效果 等等 ; back 服務(wù)穩(wěn)定,性能,內(nèi)存泄漏等等
V8 內(nèi)存的簡(jiǎn)介首先通過memoryUsage可以查看node進(jìn)程的使用情況,具體介紹如下
process.memoryUsage(); 查看node進(jìn)程內(nèi)存使用情況 單位是字節(jié) { rss: 20725760, (resident set size)進(jìn)程的常駐內(nèi)存 heapTotal: 7376896, 已申請(qǐng)到的堆內(nèi)存 heapUsed: 3881280, 當(dāng)前使用的堆內(nèi)存 external: 8772 , c++的內(nèi)存使用,受v8管理 }
針對(duì)上面api使用寫了個(gè)相關(guān)例子
let showMem=function () { var mem=process.memoryUsage(); var format=function (bytes) { return (bytes/1024/1024).toFixed(2)+"MB"; } console.log("Process :heapTotal "+format(mem.heapTotal)+" heapUsed "+ format(mem.heapUsed)+" rss "+format(mem.rss)); console.log("------------------------------------") } var useMem=function () { var size=200*1024*1024; //每次構(gòu)建200MB對(duì)象 var buffer=new Buffer(size); for(var i=0;iheapTotal和heapUsed變化極小,唯一變化rss值,且該值遠(yuǎn)超V8內(nèi)存分配限制(說明:V8的內(nèi)存限制:64位系統(tǒng)約為1.4GB、32位系統(tǒng)約為0.7GB (這個(gè)規(guī)定是node源碼 中限制的),具體測(cè)試可以將上述實(shí)例中buffer改成array即可)
說明:node 內(nèi)存由通過v8進(jìn)行分配的部分(新生到和老生代)(只有這部分才會(huì)受v8垃圾回收的限制)和node進(jìn)行自行分配的部分(例如buffer)
額外補(bǔ)充
--max-old-space-size 命令就是設(shè)置老生代內(nèi)存空間的最大值
--max-new-space-size 命令則可以設(shè)置新生代內(nèi)存空間的大小
這兩個(gè)參數(shù)只能在node啟動(dòng)時(shí)進(jìn)行設(shè)置
下面從gc層面談?wù)撓聉8內(nèi)存,這個(gè)可以說很多,我的云筆記中關(guān)于java和js的內(nèi)存使用,分配,gc等整理了好幾個(gè)系列,下面我用自己的話簡(jiǎn)單總結(jié)下。
關(guān)于內(nèi)存分區(qū)有以下兩個(gè)大類:
new space(特征:對(duì)象存活時(shí)間短) 又分為from和to兩個(gè)區(qū)域,采用復(fù)制算法,空間換時(shí)間。如果存活多次,轉(zhuǎn)移到老生代中。
老生代中(對(duì)象存活時(shí)間長(zhǎng)),采用mark-sweep(標(biāo)記清除)和mark-compact(標(biāo)記整理),缺點(diǎn)容易形成內(nèi)存碎片,導(dǎo)致無法分配大對(duì)象。v8主要使用mark-sweep,在內(nèi)存空間不夠時(shí),才使用標(biāo)記整理。老生代又可以細(xì)分Old Space(新生代中g(shù)c活過2次晉升到這個(gè)空間)、Large Object Space(大對(duì)象,一般指超過1MB,初始時(shí)直接分配到此),Map Space(“隱藏類”的指針,便于快速訪問對(duì)象成員)、Code Space(機(jī)器碼,存儲(chǔ)在可執(zhí)行內(nèi)存中)
關(guān)于gc,總的來說在js中不同對(duì)象存活時(shí)間不同,沒有一種算法適應(yīng)所有場(chǎng)景,內(nèi)存垃圾進(jìn)行分代,針對(duì)不同分代使用相應(yīng)高效算法,有些gc算法和java是一樣的,復(fù)制(空間換時(shí)間,new Space),標(biāo)記清除和標(biāo)記整理(old space)等等。
補(bǔ)充存活標(biāo)記依據(jù)
全局變量或者有由全局變量出發(fā),可以訪問到的對(duì)象
正在執(zhí)行的函數(shù)中的局部對(duì)象,包括這些局部對(duì)象可以訪問到的對(duì)象。
閉包中引用的對(duì)象
關(guān)于內(nèi)存,偏底層語言(例如c)和js以及java不一樣
#c代碼 #includevoid init() { int arr[5]={}; for(int i=0;i<5;i++){ arr[i]=i; } } void test(){ int arr[5]; for(int i =0;i<5;i++){ printf("%d ",arr[i]); } } int main(int argc,char const *argv[]){ init(); test(); return 0; } // 0 1 2 3 4 # java代碼 public class Test { public static void main(String[] args){ init(); test(); } public static void init(){ int[] arr=new int[]{0,1,2,3,4}; // for(int i=0;i<5;i++){ // System.out.println(arr[i]); // } } public static void test(){ int[] arr=new int[5]; for(int i=0;i<5;i++){ System.out.println(arr[i]); } } } // 0 0 0 0 0 上述例子說明c語言堆棧執(zhí)行過程中,函數(shù)執(zhí)行完堆棧釋放,程序員如果不關(guān)注釋放過程,出現(xiàn)一些問題 ,
內(nèi)存泄漏
而js和java就不會(huì),因?yàn)閮?nèi)存自動(dòng)分配,垃圾自動(dòng)回收,不用考慮臟數(shù)據(jù)擦除。首先什么是內(nèi)存泄漏?
對(duì)象不再被應(yīng)用程序使用,但是垃圾回收器卻不能移除它們,因?yàn)樗鼈冋诒灰谩?/p>
node進(jìn)程運(yùn)行中g(shù)arbage沒被回收肯定屬于內(nèi)存泄漏,關(guān)于這個(gè)定義引起討論是在node進(jìn)程退出或崩潰后的情況。我的理解是如果有部分內(nèi)存比如共享內(nèi)存(用于進(jìn)程間通信),如果沒被釋放,這依然屬于內(nèi)存泄漏,內(nèi)存泄漏不僅僅只進(jìn)程層面。
內(nèi)存泄漏原因有好幾種,ppt有的,我不在列舉,我在分享過程中有同學(xué)提出個(gè)疑問,關(guān)于exports使用中為什么會(huì)導(dǎo)致泄漏,印象比較深刻,可能當(dāng)時(shí)講的不清楚,下面寫個(gè)具體例子詳細(xì)闡述下。
//A.js var leakAry=[]; exports.leak=function(){ leakAry.push("leak "+" gcy "+new Date()); } //B.js var obj=require("./a"); for(var i=0;i<10;i++){ obj.leak(); }在node當(dāng)中,為了加速module訪問,所有module都會(huì)被編譯緩存,上述代碼導(dǎo)致泄漏的原因就是模塊上,被緩存局部變量重復(fù)訪問,因?yàn)闆]初始化,導(dǎo)致內(nèi)存占用不斷增大。
其次平時(shí)如何定位內(nèi)存泄漏具體問題。
var http=require("http"); var heapdum=require("heapdump"); var leakArray=[]; var leak=function () { for(var i=0;i<100000;i++){ leakArray.push("leak "+Math.random()); } }; var i=0; http.createServer(function (req,res) { leak(); //泄漏觸發(fā)位置 i++; res.writeHead(200,{"Content-Type":"text/plain"}); res.end("hello world gcy"+i); }).listen(1337); console.log(process.pid); console.log("server start gcy"); ------------------------------------------------------------- for ((i=1;i<=10000;i++)); do curl -v --header "Connection: keep-alive" "http://127.0.0.1:1337/" done 批量100和10000個(gè)請(qǐng)求,記錄heap dump鏡像,通過對(duì)比視圖查看變化比較大地方,通過分析定位具體泄漏位置,展示效果如下圖可以看到有三處對(duì)象明顯增長(zhǎng)的地方,string、concatenated string以及 array 對(duì)象增長(zhǎng)。點(diǎn)擊查看一下對(duì)象的引用情況,可以發(fā)現(xiàn)原因是leak執(zhí)行,leakArray沒有初始化,導(dǎo)致其里面字符串沒有被清除,從而導(dǎo)致內(nèi)存泄漏。
關(guān)于異步處理問題:不適合處理復(fù)雜的狀態(tài)機(jī)
解決方案:是使用隊(duì)列結(jié)構(gòu)進(jìn)行可控的異步并發(fā)。
關(guān)于這一點(diǎn)理解分享中上有人提出可異議,
弱計(jì)算
我的理解是比如邏輯中有大量的promise待處理,一旦此邏輯比較多,我們沒法掌控,宏觀上沒法知曉具體執(zhí)行情況,但是通過隊(duì)列,在高并發(fā)請(qǐng)求下,大量的狀態(tài)機(jī)promise通過隊(duì)列
的管理,我們可以做到可控,哪些被消費(fèi)了,狀態(tài)機(jī)所處某個(gè)過程的比例都可以統(tǒng)計(jì),一旦有這些統(tǒng)計(jì)信息,我們就可以進(jìn)行相應(yīng)的處理。求證。什么是 IO 密集型? 控制器busy
什么是 CPU 密集型? 運(yùn)算器busy
關(guān)于弱計(jì)算的分析可以用node生成profile文件,然后通過chrome進(jìn)行分析或者webstrome自帶的v8 profiling進(jìn)行分析,可以得到一系列函數(shù)執(zhí)行時(shí)間統(tǒng)計(jì)和調(diào)用堆棧過程時(shí)間消耗統(tǒng)計(jì),依據(jù)這些信息,我們?cè)谧鱿鄳?yīng)的優(yōu)化。圖中顯示的是例子test2的結(jié)果。
部署 child_process//普通情況,只是作為用法示例 var fork=require("child_process").fork; var cpus=require("os").cpus(); for(var i=0;i總結(jié) child_process 模塊給node提供了一下幾個(gè)方法創(chuàng)建子進(jìn)程
1: spwan(); 啟動(dòng)一個(gè)子進(jìn)程執(zhí)行命令
2: exec() 啟動(dòng)一個(gè)子進(jìn)程執(zhí)行命令,與spwan不同的是,他有一個(gè)回調(diào)函數(shù)獲知子進(jìn)程狀況
3: fork() 與spwan類似 不同地方在于 在創(chuàng)建子進(jìn)程的時(shí)候只需指定 需要執(zhí)行的JavaScript文件即可上面方法很少用了,node當(dāng)中有cluster模塊,簡(jiǎn)單的幾個(gè)方法就可以創(chuàng)建集群,其本質(zhì)基于上面的封裝,底層實(shí)現(xiàn)原理基于句柄共享。
var index=require("./app"); if (cluster.isMaster) for (var i = 0, n = os.cpus().length; i < n; i += 1) cluster.fork(); else index.app(); ------------------------------------------- //app.js function app() { var server = http.createServer(function(req, res) { res.writeHead(200); res.end("hello world gcy "); console.log(cluster.worker.id); }); server.listen(8088); } exports.app=app;cluster使用中有三個(gè)問題
1、round-robin是當(dāng)前cluster的默認(rèn)負(fù)載均衡處理模式(除了windows平臺(tái)),自行設(shè)定負(fù)載算法
可以在cluster加載之后未調(diào)用其它c(diǎn)luster函數(shù)之前執(zhí)行:cluster.schedulingPolicy = cluster.SCHED_NONE。2、進(jìn)程監(jiān)控問題
master進(jìn)程不會(huì)自動(dòng)管理worker進(jìn)程的生死,如果worker被外界殺掉了,不會(huì)自動(dòng)重啟,
只會(huì)給master進(jìn)程發(fā)送‘exit’消息,開發(fā)者需要自己做好管理。3、數(shù)據(jù)共享問題
各個(gè)worker進(jìn)程之間是獨(dú)立的,為了讓多個(gè)worker進(jìn)程共享數(shù)據(jù)(譬如用戶session),
一般的做法是在Node.js之外使用memcached或者redis。cluster適用于在單臺(tái)機(jī)器上,如果應(yīng)用的流量巨大,多機(jī)器是必然的。這時(shí),反向代理就派上用場(chǎng)了,我們可以用node來寫反向代理的服務(wù)(比如用 http-proxy )
,好處是可以保持工程師技術(shù)棧的統(tǒng)一,不過生產(chǎn)環(huán)境,我們用的更多的還是nginx,部分重要配置如下。nginx做集群
upstream gcy.com{ server 127.0.0.1:3000 weight=1; server 127.0.0.1:3001 weight=2; server 127.0.0.1:3002 weight=6; }維護(hù)維護(hù)主要做好以下三點(diǎn)
日志
異常處理
第三方依賴管理。
異常處理解釋下,有人提出疑問:異常沒有被捕獲一路冒泡 ,會(huì)觸發(fā)uncaughtException 事件,
總結(jié)
如果異常出現(xiàn)之后,沒有進(jìn)行正確的恢復(fù)操作可能導(dǎo)致內(nèi)存泄漏,清理已使用的資源
(文件描述符(清除文件的占用)、句柄(Master傳給work的標(biāo)識(shí))等) 然后 process.exit。qcon現(xiàn)場(chǎng)聽一次,自己分享一次,總結(jié)一次,感覺收貨蠻大的,有些地方自己加深了理解,分享中front to back開頭部分,原先8頁太多,本應(yīng)一筆帶過,講的時(shí)候廢話太多,原本我只想闡述其中幾個(gè)關(guān)鍵名詞。
分享本身是一個(gè)學(xué)習(xí),開拓眼界的的過程,切身體會(huì),還是需要自己實(shí)踐,寫具體case。
上述基本上是我講的過程記錄,做個(gè)總結(jié),以便回顧。
演示部分鏈接, demo
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/82870.html
摘要:前戲補(bǔ)上參會(huì)的完整記錄,這個(gè)問題從一開始我就是準(zhǔn)備自問自答的,希望可以通過這種形式把大會(huì)的干貨分享給更多人。 showImg(http://7xqy7v.com1.z0.glb.clouddn.com/colorful/blog/feday2.png); 前戲 2016/3/21 補(bǔ)上參會(huì)的完整記錄,這個(gè)問題從一開始我就是準(zhǔn)備自問自答的,希望可以通過這種形式把大會(huì)的干貨分享給更多人。 ...
閱讀 1751·2021-09-23 11:34
閱讀 2472·2021-09-22 15:45
閱讀 12821·2021-09-22 15:07
閱讀 2221·2021-09-02 15:40
閱讀 4107·2021-07-29 14:48
閱讀 1071·2019-08-30 15:55
閱讀 3245·2019-08-30 15:55
閱讀 2190·2019-08-30 15:55