国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > JavaScript > 正文

淺析Node.js中的內存泄漏問題

2019-11-20 12:11:17
字體:
來源:轉載
供稿:網友

 這篇文章是由Mozilla的Identity團隊帶來的 A Node.JS Holiday Season系列文章的首篇,該團隊上個月發布了 Persona的第一個測試版本。在開發Persona時我們構建了一系列的工具,包括了從調試,到本地化,到依賴管理以及更多的方面。在這一系列的文章中我們將與社區分享我們的經驗和這些工具,這對任何想用node.js建立一個高可用性服務的人都很有用。我們希望您能喜歡這些文章,并期待看到您的想法和貢獻。

我們將從一篇關于Node.js的實質性問題:內存泄漏的主題文章開始。我們會介紹 node-memwatch ― 一個幫助發現并隔離Node中的內存泄漏問題的函數庫。


為什么自尋煩惱?

關于追蹤內存泄漏問得最多的問題就是,“為什么要自尋煩惱?”。難道沒有更緊迫的問題需要先解決嗎?為什么不選擇不時地重啟服務,或為之分配更多的RAM?為了回答這些問題,我們提出了以下三點建議:

1.也許你不在乎不斷增長的內存占用,但V8在乎(V8是Node運行時的引擎)。隨著內存泄漏的增長,V8對垃圾收集器越來越具有攻擊性,這會使你的應用運行速度變慢。所以,在Node上,內存泄漏會損害程序性能。

2.內存泄漏可能觸發其他類型的失敗。內存泄漏的代碼可能會持續的引用有限的資源。你可能會耗盡文件描述符;你還可能會突然不能建立新的數據庫連接。這類問題可能在你的應用耗盡內存前很早就會暴露出來,但它仍然會是你陷入困境。

3.最后,你的應用遲早會崩潰,并且在你的應用受到歡迎時肯定會發生。所有人都會在Hacker News上嘲笑你,諷刺你,這樣你就悲劇了。

潰千里之堤的蟻穴在哪里?

在構建復雜應用的時候,很多地方都可能發生內存泄露。 閉包可能是最廣為人知也是最聲名狼藉的。因為閉包保留了對其作用域內的東西的引用,而這正是通常的內存泄露之源。

閉包泄露往往只有在有人去尋找它們的時候才能發現。但是在Node的異步世界里,我們隨時隨地的通過回調函數不停的生成閉包。如果這些回調函數沒有在創建后立刻使用,分配的內存就會持續增長,那些看起來沒有內存泄露問題的代碼也會產生泄露。而這種問題更難發現。

你的應用也可能由于上游代碼的問題導致內存泄露。也許你能定位到出現內存泄露的代碼,但是你可能只能眼巴巴地盯著你那完美無缺的代碼然后困惑于這到底是怎么泄露的!


正是這些難以定位的內存泄露促使我們想要一個node-memwatch這樣的工具。傳說幾個月以前,我們的Lloyd Hilaiel把他自己鎖在一個小房間里兩天,試著追蹤一個在壓力測試下變得非常明顯的內存泄露問題。(順便說下,盡請期待Lloyd即將到來的關于負荷測試的文章)

經過兩天的努力,他終于發現了Node內核中的元兇:http.ClientRequest中的事件監聽器沒有被釋放。(最終修復這個問題的補丁只有兩個但卻至關重要的字母)。正是這次痛苦的經歷促使Lloyd想要寫一個能夠幫助查找內存泄露的工具。

內存泄露定位工具

現在已經有許多好用且不斷增強的工具用于定位Node.js應用的內存泄露。下面是其中的一些:

上面的這些工具我們都很喜歡,但是沒有一個適用于我們的場景。Web Inspector對于開發中的應用非常棒,但是很難用于熱部署的場景,尤其是在多服務器和涉及子進程的時候。同樣的,在長時間高負載運行中出現的內存泄露也很難復現。像dtrace和libumem這樣的工具雖然讓人印象深刻,但是不是所有的操作系統都能用。

Enternode-memwatch

我們需要一個跨平臺的調試庫,當我們的程序可能存在內存泄漏時,它不需要設備告訴我們,并且會幫我們找到哪里存在泄漏。所以我們實現了node-memwatch。

它給我們提供三件東西:

    一個‘泄漏'事件發射器
   

   memwatch.on('leak', function(info) {  // look at info to find out about what might be leaking  });

    一個‘狀態事件發射器
   
     

  var memwatch = require('memwatch');  memwatch.on('stats', function(stats) {  // do something with post-gc memory usage stats  });

    一個堆內存區分類
   

  var hd = new memwatch.HeapDiff();  // your code here ...  var diff = hd.end();

    并且還有一個在測試時很有用處的,可以觸發垃圾收集器的功能。好吧,一共四點。
   
  

 var stats = memwatch.gc();

memwatch.on('stats', ...): Post-GC堆統計

node-memwatch能夠在任何一個JS對象分配之前,緊隨著一次完整的垃圾回收和內存壓縮發出一個內存使用樣本。(它使用了V8的post-gc鉤子,V8::AddGCEpilogueCallback,來在每次垃圾回收觸發時收集堆使用信息)

統計數據包括:

  •     usage_trend(使用趨勢)
  •     current_base(當前基數)
  •     estimated_base(預期基數)
  •     num_full_gc (完整的垃圾回收次數)
  •     num_inc_gc (增長的垃圾回收次數)
  •     heap_compactions (內存壓縮次數)
  •     min (最小)
  •     max (最大)

這里有一個展示存在內存泄露的應用的數據看起來是什么樣的例子。下面的圖表隨著時間追蹤內存的使用。瘋狂的綠線展示了process.memoryUsage()報告的內容。紅線展示了node_memwatch報告的current_base。左下側的盒子展示了附加信息。

2015623152204606.png (572×441)

 注意Incr GCs非常高。那說明V8在拼命的嘗試清理內存。

memwatch.on('leak', ...): 堆分配趨勢

我們定義了一個簡單的偵測算法來提醒你應用程序可能存在內存泄漏。即如果經過連續五次GC,內存仍被持續分配而沒有得到釋放,node-memwatch就會發出一個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' }

memwatch.HeapDiff(): 查找泄漏元兇

最后,node-memwatch能比較堆上對象的名稱和分配數量的快照,其對比前后的差異可以幫助找出導致內存泄漏的元兇。
 

var hd = new memwatch.HeapDiff(); // Your code here ... var diff = hd.end();

對比產生的內容就像這樣:
 

{ "before": {  "nodes": 11625,  "size_bytes": 1869904,  "size": "1.78 mb" }, "after": {  "nodes": 21435,  "size_bytes": 2119136,  "size": "2.02 mb" }, "change": {  "size_bytes": 249232,  "size": "243.39 kb",  "freed_nodes": 197,  "allocated_nodes": 10007,  "details": [   {    "what": "Array",    "size_bytes": 66688,    "size": "65.13 kb",    "+": 4,    "-": 78   },   {    "what": "Code",    "size_bytes": -55296,    "size": "-54 kb",    "+": 1,    "-": 57   },   {    "what": "LeakingClass",    "size_bytes": 239952,    "size": "234.33 kb",    "+": 9998,    "-": 0   },   {    "what": "String",    "size_bytes": -2120,    "size": "-2.07 kb",    "+": 3,    "-": 62   }  ] }}

HeapDiff方法在進行數據采樣前會先進行一次完整的垃圾回收,以使得到的數據不會充滿太多無用的信息。memwatch的事件處理會忽略掉由HeapDiff觸發的垃圾回收事件,所以在stats事件的監聽回調函數中你可以安全地調用HeapDiff方法。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 兰西县| 黔西县| 芷江| 长武县| 金平| 宁津县| 四子王旗| 封开县| 乳源| 行唐县| 彰化市| 云南省| 招远市| 嘉祥县| 镇江市| 都昌县| 香港| 兰坪| 镇远县| 邵武市| 贞丰县| 乌鲁木齐县| 南通市| 黄石市| 西乌| 磐安县| 襄樊市| 钟祥市| 甘泉县| 咸阳市| 类乌齐县| 绥中县| 绥滨县| 望奎县| 尼勒克县| 綦江县| 临海市| 衡阳市| 普宁市| 禄劝| 循化|