之前一次偶然機會發現,react 在server渲染時,當NODE_ENV != production時,會導致記憶體洩漏。具體issues: https://github.com/facebook/react/issues/7406 。隨著node,react同構等技術地廣泛運用,node端記憶體洩漏等問題應該引起我們的重視。為什麼node容易出現記憶體洩漏以及出現之後應該如何排查,以下透過一個簡單的介紹以及例子來說明。
首先,node是基於v8引擎基礎上,其記憶體管理方式與v8一致。以下簡單介紹v8的相關記憶體特效。
V8記憶體限制
node基於V8構建,透過V8的方式進行分配跟管理js物件。 V8對記憶體的使用有限制(老生代記憶體64位元系統下約為1.4G,32位元系統下約為0.7G,新生代記憶體64位元系統下約32MB,32系統下約為16MB)。在這樣的限制下,將導致無法操作大內存物件。如果不小心觸碰這個界限,就會造成進程退出。
原因:V8執行垃圾回收時會阻塞JavaScript應用邏輯,直到垃圾回收結束再重新執行JavaScript應用邏輯,這種行為被稱為「全停頓」(stop-the-world)。若V8的堆內存為1.5GB,V8做一次小的垃圾回收需要50ms以上,做一次非增量式的垃圾回收甚至要1秒以上。
透過node --max-old-space-size=xxx(單位MB) , node --max-new-space-size=xxx(單位KB) 設定新生代記憶體以及老生代記憶體來破解預設的記憶體限制。
V8的堆構成
V8的堆其實並不只是由老生代和新生代兩部分構成,可以將堆分為幾個不同的區域:
新生代內存區:大多數的對像被分配在這裡,這個區域很小但是垃圾回特別頻繁
老生代指針區:屬於老生代,這裡包含了大多數可能存在指向其他對象的指針的對象,大多數從新生代晉升的對象會被移動到這裡
老生代資料區:屬於老生代,這裡只保存原始資料對象,這些對象沒有指向其他對象的指針
大對象區:這裡存放體積超越其他區大小的對象,每個對像有自己的內存,垃圾回收其不會移動大對象
代碼區:代碼對象,也就是包含JIT之後指令的對象,會被分配在這裡。唯一擁有執行權限的記憶體區
Cell區、屬性Cell區、Map區:存放Cell、屬性Cell和Map,每個區域都是存放相同大小的元素,結構簡單
GC回收類型
增量式GC
表示垃圾回收器在掃描記憶體空間時是否收集(增加)垃圾並在掃描週期結束時清空垃圾。
非增量式GC
使用非增量式垃圾收集器時,一收集到垃圾即將其清空。
垃圾回收器只會針對新生代記憶區、老生代指針區以及老生代資料區進行垃圾回收。物件首先進入佔用空間較少的新生代記憶體。大部分物件會很快失效,非增量GC直接回收這些少量記憶體。假如有些物件一段時間內不能被回收,則進去老生代內存區。這個區域則執行不頻繁的增量GC,且耗時較長。
那什麼時候才會導致記憶體洩漏的發生呢?
記憶體洩漏的途徑
記憶體洩漏
快取
隊列消費不及時
作用域未釋放
Node的記憶體構成主要是透過V8進行分配的部分和Node自行分配的部分。受V8的垃圾回收限制的主要是V8的堆內存。造成記憶體洩漏的主因:1,快取;2,佇列消費不及時;3,作用域未釋放
記憶體洩漏分析
查看V8記憶體使用量(單位byte)
process.memoryUsage(); { ress: 47038464, heapTotal: 34264656, heapUsed: 2052866 }
進程的常駐記憶體部分heapTotal,heapUsed:V8堆內存資訊查看系統記憶體使用情況(單位byte)
os.totalmem()
os.freemem()
os.totalmem()
os.freemem()os.totalmem()os.freemem()
node --prof //輸出node執行時效能日誌。 使用windows-tick.processor查看。
分析監控工具
v8-profiler 對v8堆內存抓取快照和對cpu進行分析
node-heapdump 對v8堆內存抓取快照node-mtrace 分析堆棧使用
nodede-memwatch node-memwatchmemwatch.on('stats',function(info){ console.log(info) }) memwatch.on('leak',function(info){ console.log(info) })stats事件:每次進行全堆垃圾回收時,將觸發一次stats事件。這個事件將會傳遞記憶體統計資訊。
{ "num_full_gc": 17, //第几次全栈垃圾回收 "num_inc_gc": 8, //第几次增量垃圾回收 "heap_compactions": 8, //第几次对老生代进行整理 "estimated_base": 2592568, //预估基数 "current_base": 2592568, //当前基数 "min": 2499912, //最小 "max": 2592568, //最大 "usage_trend": 0 //使用趋势 }觀察num_full_gc和num_inc_gc反映垃圾回收。 leak事件:如果經過連續5次垃圾回收後,記憶體仍然沒有被釋放,意味著記憶體洩漏的發生。這時候會觸發一個leak事件。 🎜
{ start: Fri, 29 Jun 2012 14:12:13 GMT, end: Fri, 29 Jun 2012 14:12:33 GMT, growth: 67984, reason: 'heap growth over 5 consecutive GCs (20s) - 11.67 mb/hr' }🎜 🎜🎜🎜
Heap Diffing 堆内存比较 排查内存溢出代码。
下面,我们通过一个例子来演示如何排查定位内存泄漏:
首先我们创建一个导致内存泄漏的例子:
//app.js var app = require('express')(); var http = require('http').Server(app); var heapdump = require('heapdump'); var leakobjs = []; function LeakClass(){ this.x = 1; } app.get('/', function(req, res){ console.log('get /'); for(var i = 0; i < 1000; i++){ leakobjs.push(new LeakClass()); } res.send('<h1 id="Hello-nbsp-world">Hello world</h1>'); }); setInterval(function(){ heapdump.writeSnapshot('./' + Date.now() + '.heapsnapshot'); }, 3000); http.listen(3000, function(){ console.log('listening on port 3000'); });
这里我们通过设置一个不断增加且不回被回收的数组,来模拟内存泄漏。
通过使用heap-dump模块来定时纪录内存快照,并通过chrome开发者工具profiles来导入快照,对比分析。
我们可以看到,在浏览器访问 localhost:3000 ,并多次刷新后,快照的大小一直在增长,且即使不请求,也没有减小,说明已经发生了泄漏。
接着我们通过过chrome开发者工具profiles, 导入快照。通过设置comparison,对比初始快照,发送请求,平稳,再发送请求这3个阶段的内存快照。可以发现右侧new中LeakClass一直增加。在delta中始终为正数,说明并没有被回收。
小结
针对内存泄漏可以采用植入memwatch,或者定时上报process.memoryUsage内存使用率到monitor,并设置告警阀值进行监控。
当发现内存泄漏问题时,若允许情况下,可以在本地运行node-heapdump,使用定时生成内存快照。并把快照通过chrome Profiles分析泄漏原因。若无法本地调试,在测试服务器上使用v8-profiler输出内存快照比较分析json(需要代码侵入)。
需要考虑在什么情况下开启memwatch/heapdump。考虑heapdump的频度以免耗尽了CPU。 也可以考虑其他的方式来检测内存的增长,比如直接监控process.memoryUsage()。
当心误判,短暂的内存使用峰值表现得很像是内存泄漏。如果你的app突然要占用大量的CPU和内存,处理时间可能会跨越数个垃圾回收周期,那样的话memwatch很有可能将之误判为内存泄漏。但是,这种情况下,一旦你的app使用完这些资源,内存消耗就会降回正常的水平。所以需要注意的是持续报告的内存泄漏,而可以忽略一两次突发的警报。
更多nodeJs記憶體洩漏問題詳解相关文章请关注PHP中文网!

JavaScript在Web開發中的主要用途包括客戶端交互、表單驗證和異步通信。 1)通過DOM操作實現動態內容更新和用戶交互;2)在用戶提交數據前進行客戶端驗證,提高用戶體驗;3)通過AJAX技術實現與服務器的無刷新通信。

理解JavaScript引擎內部工作原理對開發者重要,因為它能幫助編寫更高效的代碼並理解性能瓶頸和優化策略。 1)引擎的工作流程包括解析、編譯和執行三個階段;2)執行過程中,引擎會進行動態優化,如內聯緩存和隱藏類;3)最佳實踐包括避免全局變量、優化循環、使用const和let,以及避免過度使用閉包。

Python更適合初學者,學習曲線平緩,語法簡潔;JavaScript適合前端開發,學習曲線較陡,語法靈活。 1.Python語法直觀,適用於數據科學和後端開發。 2.JavaScript靈活,廣泛用於前端和服務器端編程。

Python和JavaScript在社區、庫和資源方面的對比各有優劣。 1)Python社區友好,適合初學者,但前端開發資源不如JavaScript豐富。 2)Python在數據科學和機器學習庫方面強大,JavaScript則在前端開發庫和框架上更勝一籌。 3)兩者的學習資源都豐富,但Python適合從官方文檔開始,JavaScript則以MDNWebDocs為佳。選擇應基於項目需求和個人興趣。

從C/C 轉向JavaScript需要適應動態類型、垃圾回收和異步編程等特點。 1)C/C 是靜態類型語言,需手動管理內存,而JavaScript是動態類型,垃圾回收自動處理。 2)C/C 需編譯成機器碼,JavaScript則為解釋型語言。 3)JavaScript引入閉包、原型鍊和Promise等概念,增強了靈活性和異步編程能力。

不同JavaScript引擎在解析和執行JavaScript代碼時,效果會有所不同,因為每個引擎的實現原理和優化策略各有差異。 1.詞法分析:將源碼轉換為詞法單元。 2.語法分析:生成抽象語法樹。 3.優化和編譯:通過JIT編譯器生成機器碼。 4.執行:運行機器碼。 V8引擎通過即時編譯和隱藏類優化,SpiderMonkey使用類型推斷系統,導致在相同代碼上的性能表現不同。

JavaScript在現實世界中的應用包括服務器端編程、移動應用開發和物聯網控制:1.通過Node.js實現服務器端編程,適用於高並發請求處理。 2.通過ReactNative進行移動應用開發,支持跨平台部署。 3.通過Johnny-Five庫用於物聯網設備控制,適用於硬件交互。

我使用您的日常技術工具構建了功能性的多租戶SaaS應用程序(一個Edtech應用程序),您可以做同樣的事情。 首先,什麼是多租戶SaaS應用程序? 多租戶SaaS應用程序可讓您從唱歌中為多個客戶提供服務


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

VSCode Windows 64位元 下載
微軟推出的免費、功能強大的一款IDE編輯器

記事本++7.3.1
好用且免費的程式碼編輯器

MinGW - Minimalist GNU for Windows
這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。

WebStorm Mac版
好用的JavaScript開發工具

SublimeText3 Linux新版
SublimeText3 Linux最新版