js日期比較大小_node.js 內存泄漏的秘密

每日前端夜話第276篇

翻譯:瘋狂的技術宅

作者:Giovanny Gongora

來源:nodesource

3040a7d13d94c06013a959c6b22d0a82.gif

正文共:3955?字

預計閱讀時間:10分鐘

e98ac923b34c5cd11147e342cedafb08.png

一直以來,跟蹤 Node.js 的內存泄漏是一個反復出現的話題,人們始終希望對其復雜性和原因了解更多。

并非所有的內存泄漏都顯而易見。但是,一旦我們確定了其模式,就必須在內存使用率,內存中保存的對象和響應時間之間尋找關聯。在檢查對象時,應該根據自己所用的框架或技術(例如服務器端渲染),研究收集了多少對象,以及它們是否正常。希望在完成本文結束之后,你將能夠理解并尋找一種策略來調試 Node.js 程序的內存消耗。

Node.js 中的垃圾回收機制

JavaScript 是一種垃圾回收語言,而 Google 的 V8 最初是為 Google Chrome 創建的JavaScript引擎,在許多情況下都可以用作獨立的運行時。Node.js 中垃圾收集器的兩個重要操作是:

  1. 確定有用的或無用的對象,并且

  2. 回收或重用無用對象所占用的內存。

需要記住的要點:在垃圾回收器運行時,它將完全暫停你的程序,直到完成工作為止。因此,你需要通過維護對象的引用來最大程度地減少其工作。

V8 JavaScript 引擎會自動分配和取消分配 Node.js 進程使用的所有內存。讓我們看看實際情況是怎樣的。

如果你將內存視為一個樹結構,那么可以想象 V8 從“根節點”開始保存程序中所有的變量。這可能是你的 window 對象,也可能是 Node.js 模塊中的全局對象,通常稱為控制者。需要牢記的一點是,你無法對怎樣取消分配“根”節點進行控制。

286ced6203fb90b40950fe3f3418df7d.png

接下來,你將找到一個 Object 節點,通常被稱為葉子(沒有子引用的節點)。最后 JavaScript 中有 4 種數據類型:布爾值,字符串,數字和對象。

V8 將遍歷該樹并嘗試識別無法從“根”節點訪問的數據組。如果無法從“根”節點訪問該數據,則 V8 假定不再使用該數據,并釋放內存。請記住:要確定某個對象是否處于活動狀態,需要檢查是否可通過被定義為活動對象的某個指針鏈到達;其他所有的情況,例如無法從根節點訪問,或無法被根節點或另一個活動對象引用的對象,都會被視為垃圾。

簡而言之,垃圾收集器有兩個主要任務:

  1. 跟蹤

  2. 計算對象之間的引用。

當你需要跟蹤來自另一個進程的遠程引用時,它可能會變得很棘手,但是在 Node.js 程序中,我們通常用單進程,這樣使我們更加輕松。

V8 的內存方案

V8 使用類似于 Java 虛擬機的方案,并將內存劃分為多個段。實現這種包裝方案的東西被稱為“駐留集”,它是指在 RAM 中駐留的進程所占用的內存部分。

在駐留集中,你會發現:

  • 代碼段:代碼實際執行的位置。

  • 棧: 包含局部變量和所有值類型,其指針引用堆上的對象或定義程序的控制流。

  • 堆: 專門用于存儲引用類型(如對象、字符串和閉包)的內存段。

62ec374bc6b6ce68d1b546b87fb75195.png

還有重要的兩點要記住:

  • 對象的淺大小:保存對象本身所需的內存大小

  • 對象的保留大小:當刪除對象及其依賴對象時,被釋放的內存大小

Node.js 有一個對象,以字節為單位描述 Node.js 進程的內存使用情況。在對象內部,你會發現:

  • rss: 是指駐留集大小。

  • heapTotal 和 heapUsed: 是指 V8 的內存使用情況。

  • external: 是指與 V8 所管理的 JavaScript 對象綁定的 C++ 對象的內存使用情況。

查找泄漏

Chrome DevTools 是一個很棒的工具,可用于通過遠程調試來診斷 Node.js 程序中的內存泄漏。也有其他為你提供類似功能的工具。但是,你需要記住,概要分析是一項繁重的 CPU 任務,可能會對你的程序產生負面影響,一定要注意這一點!

我們將要介紹的 Node.js 程序是一個簡單的 HTTP API Server,它具有多個端點,向使用該服務的人返回不同的信息。你可以克隆這個程序的repository。

 1const?http?=?require('http')
2
3const?leak?=?[]
4
5function?requestListener(req,?res)?{
6
7??if?(req.url?===?'/now')?{
8????let?resp?=?JSON.stringify({?now:?new?Date()?})
9????leak.push(JSON.parse(resp))
10????res.writeHead(200,?{?'Content-Type':?'application/json'?})
11????res.write(resp)
12????res.end()
13??}?else?if?(req.url?===?'/getSushi')?{
14????function?importantMath()?{
15??????let?endTime?=?Date.now()?+?(5?*?1000);
16??????while?(Date.now()?17????????Math.random();
18??????}
19????}
20
21????function?theSushiTable()?{
22??????return?new?Promise(resolve?=>?{
23????????resolve('?');
24??????});
25????}
26
27????async?function?getSushi()?{
28??????let?sushi?=?await?theSushiTable();
29??????res.writeHead(200,?{?'Content-Type':?'text/html;?charset=utf-8'?})
30??????res.write(`Enjoy!?${sushi}`);
31??????res.end()
32????}
33
34????getSushi()
35????importantMath()
36??}?else?{
37????res.end('Invalid?request')
38??}
39}
40
41const?server?=?http.createServer(requestListener)
42server.listen(process.env.PORT?||?3000)

啟動Node.js應用程序:

40d9ad334177f724afe3e4db525ab5bf.png

我們一直在使用 3S(3 Snapshot)方法進行診斷并確定可能的內存問題。有趣的是,我們發現這是 Gmail 團隊的 Loreena Lee 長期使用的一種解決內存問題的方法。此方法的步驟:

  1. 打開 Chrome DevTools 并訪問 chrome://inspect

  2. 在底部的“Remote Target”中,單擊 inspect 按鈕。

008ba8942d82d39fdff81eb8bc2c12e8.png

注意: 要確保已將 Inspector 附加到要分析的 Node.js 程序。你還可以用 ndb 連接到 Chrome DevTools。

當應用運行時,你將在控制臺的輸出中看到一條 Debugger Connected 消息。

  1. 轉到 Chrome DevTools > Memory

  2. 獲取堆快照

8f1e49dd0447766092f1a4fe25d0d5ab.png

在這種情況下,我們得到了第一個快照,而服務沒有進行任何負載或處理。這是針對某些用例的提示:如果我們能夠確定在接受請求或進行某些處理之前不需要對程序進行任何預熱,那就很好了。有時,在獲取第一個堆快照之前先進行熱身操作是有意義的,因為在某些情況下,你可能會在第一次調用時對全局變量進行了延遲初始化。

  1. 在你的程序中執行你認為導致內存泄漏的操作。

在這種情況下,我們將運行 ?npm run load-mem。這將啟動 ab 來模擬 Node.js 應用程序中的流量或負載。

eb85c8a56dec246da147a44ffe90bc2f.png

  1. 得到堆快照

1f82e8f6cef5b66ecdbcbe940245bdb3.png

  1. 再次在你的程序中執行你認為會導致內存泄漏的操作。

  2. 獲取最終的堆快照

ff8bbcd10d5208fc9b772a99bdda45cc.png

  1. 選擇最新得到的快照。

  2. 在窗口頂部,找到顯示 “All objects” 的下拉列表,并將其切換為“Objects allocated between snapshots 1 and 2”。(如果需要,你也可以對 2 和 3 執行相同的操作)。這將大大減少你看到的對象數量。

0f9a1e48c256cf64e90b8450102f35da.png

比較視圖也可以幫你識別那些對象:

ec4dd579dc9154131dfaf1cd0242c9b9.png

在該視圖中,你將看到泄漏對象的列表:頂級條目(每個構造函數一行)、對象到GC根的距離、對象實例數、淺大小和保留大小。你可以通過選擇一行來查看其內容。一個好的經驗法則是,首先忽略括號中的項目,因為它們是內置結構。@ 字符是對象的唯一 ID,可讓你比較每個對象的堆快照。

典型的內存泄漏可能是通過意外地將對對象的引用存儲在無法進行垃圾回收的全局對象中,從而保留了預期僅在一個請求周期內持續存在的對象的引用。

這個例子故意留下了一個內存泄漏的問題,在請求一個從 API 查詢返回的對象時生成帶有日期時間戳的隨機對象,并將其存儲在全局數組中來泄漏該對象。通過查看幾個保留的對象,你會看到一些泄漏數據的示例,可用于跟蹤應用程序中的泄漏。

NSolid 非常適合這種類型的用例,因為它可以使你很好地了解在執行的每個任務或負載測試中內存是怎樣增加的。如果你感到好奇,還可以實時查看每個性能分析動作如何影響 CPU。

f8346e360d2a6bfcb8abb3a7ab200b71.png

demo

在實際項目中,你不可能總是盯著用于監視程序的工具。NSolid 的一大優點是可以為應用程序的不同指標設置閾值和限制。例如,你可以將 NSolid 設置為在使用的內存量超過 X 時,或者在 X 時間內尚未從高消耗高峰恢復內存的情況下,進行堆快照。聽起來不錯吧?

標記和清理

V8 的垃圾收集器主要基于 Mark-Sweep 收集算法,該算法包括跟蹤垃圾收集,該操作通過標記可達的對象,然后清理內存并回收未標記的對象(必須無法訪問),將其納入釋放列表。這也稱為世代垃圾收集器,對象可以在新聲代、從新生代到老生代、以及老生代中移動。

移動對象的代價非常打,因為需要將對象的基礎內存復制到新位置,并且指向這些對象的指針也需要更新。

用人話解釋:

V8 遞歸查找所有對象到“根”節點的引用路徑。例如:在 JavaScript 中,“window” 對象是可以充當 Root 的全局變量的示例。window 對象始終存在,因此垃圾收集器可以認為它及其所有子對象始終存在(即不是垃圾)。如果有任何引用,則沒有指向“根”節點的路徑。特別是當它以遞歸方式查找未引用的對象時,將被標記為垃圾,稍后將會被清除以釋放該內存并將其返回給操作系統。

但是,現代的垃圾收集器以不同的方式對這種算法進行了改進,但本質是相同的:可訪問的內存被標記為一類,其余的被視為垃圾。

請記住,從根可以訪問到的所有內容均不視為垃圾。不需要的引用是保留在代碼中某個位置的變量,這些變量將不再使用,并且指向可以釋放的內存,因此,要了解 JavaScript 中最常見的泄漏,我們需要了解通常忘記引用的方式。

Orinoco 垃圾收集器

Orinoco 是最新 GC 項目的代號,它利用最新的增量和并發技術進行垃圾回收,并有釋放主線程的功能。描述 Orinoco 性能的重要指標之一是垃圾回收器執行時主線程暫停的頻率和時間。對于經典的“世界末日”收集者而言,這些時間間隔會因為延遲、質量差的渲染以及響應時間的增加而影響程序的用戶體驗。

V8 在新聲代內存中的輔助流之間分配垃圾回收工作(清除)。每個流接收一組指針,然后將所有活動對象移動到“to-space”

將對象移至“to-space”時,線程需要通過讀、寫、比較和交換的原子操作進行同步,以避免出現另一個線程找到相同的對象但遵循不同路徑并嘗試移動的情況。

引用自 V8 官網:

在現有 GC 中添加并行、增量和并發技術是一項多年的努力,但已取得了回報,將大量工作移交給了后臺任務。它大大改善了暫停時間、延遲和頁面加載,使動畫、滾動和用戶交互更加順暢。并行的 Scavenger 根據工作量將主線程新聲代垃圾收集的總時間減少了大約 20%–50%。Idle-time GC 可以在 Gmail 空閑時將其 JavaScript 堆內存減少 45%。并發標記和清除可以將笨重的 WebGL 游戲中的暫停時間減少多達 50%。

Mark-Evacuate 收集器包括三個階段:標記、復制和更新指針。為了避免在新聲代中清理頁面以維護空閑列表,仍然使用 semi-space 來維護新生代,它始終保持緊湊狀態,即在垃圾回收期間將活動對象復制到 “to-space” 中。并行進行的好處是可以獲得“exact liveness”信息。通過僅移動和重新鏈接主要包含活動對象的頁面,可以用此信息來避免復制,這也可以由完整的 Mark-Sweep-Compact 收集器執行。它通過和標記清除算法相同的方式標記堆中的活動對象來工作,這意味著堆通常會被碎片化。V8 當前隨附有并行的 Scavenger,可在大量基準測試中減少主線程新生代垃圾回收約 20%–50% 的總時間

與暫停主線程、響應時間和頁面加載有關的所有方面都得到了顯著改善,這使得頁面上的動畫、滾動和用戶交互更加流暢。并行收集器可以將新內存的總處理時間減少 20–50%,具體取決于負載。但是工作還沒有結束:減少停頓仍然是一項重要任務,我們將繼續尋找使用更先進的技術來實現這一目標的可能性。

總結

大多數開發人員在開發 JavaScript 程序時無需考慮 GC,但是了解一些內部知識可以幫助你考慮內存使用情況和有用的編程模式。例如考慮到 V8 中基于世代的堆結構,從 GC 角度來說,維護低生存期的對象的成本實際上是相當低的,因為我們主要為存在的對象付出代價。這種模式不僅特定于 JavaScript,而且對于許多支持垃圾回收的語言也都有效。

要點:

  • 請勿使用過時或不推薦的軟件包(例如,node-memwatch,node-inspector 或 v8-profiler)來檢查內存。你需要的一切都已經集成在了 Node.js 的二進制文件中(尤其是 node.js 檢查器和調試器)。如果你需要更專業的工具,則可以使用 NSolid、Chrome DevTools 或其他知名軟件。

  • 考慮在何時何地觸發堆快照和 CPU profile。由于要在生產環境中進行快照,你將會希望同時觸發這兩者(主要是在測試中),所以這會需要大量的 CPU 操作。另外,在關閉進程和進行冷重啟之前,請確認有多少堆轉儲被寫入了。

  • 沒有哪一種工具可以解決所有問題。要根據程序的具體情況進行測試、測量、判斷和解決。選擇適合你體系結構的最佳工具,并選擇一種可以提供更多有用數據來幫你解決問題的工具。

原文:https://nodesource.com/blog/memory-leaks-demystified

?d25a9493f4700a8d455978f7407dd960.gif

acb6eeb9f646cddf02115a138f7c34c8.png

2020年京程一燈全新課程體系即將推出,請保持關注。

愿你在新的一年里保持技術領先,有個好前程,愿你月薪30K。我們是認真的 !b1a1aef9dcdf4f194c2d99bdd60e91fc.png

?往期精彩回顧

面向開發人員的十大 NodeJS 框架

JavaScript 類完整指南

講給前端的正則表達式

WebAssembly 正式成為 Web 的第四種語言

2020 年 Node.js 將會有哪些新功能

2020 年 Web 開發展望

從 JavaScript、ES6、ES7 到 ES10,你學到哪兒了?

15個 Vue.js 高級面試題

8680a76494769f7243033f1256eb469b.gif

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/258750.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/258750.shtml
英文地址,請注明出處:http://en.pswp.cn/news/258750.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

win7+vs2015/13+caffe+matlab+python(CPU only)配置

首先聲明本教程可以適用于vs2015 和vs2013 .以vs2015為例。 安裝必備軟件 vs 2015 /vs2013 matlab 2016a(64bit)推薦使用Anaconda 2.7 或者Miniconda 2.7這兩個Python發布版本cmake 3.8.0 以上caffe-window: https://github.com/BVLC/caffe/tree/windows 可選軟件&#xff1…

Performance Co-Pilot

Install Performance Co-Pilot 提前安裝依賴 [rootiZrj97j6t7ih9hgz1me35hZ ~]# cat install.sh yum install -y docker yum install -y git yum install -y yum-utils-1.1.31-40.el7.noarch yum install lex yum install flex yum install -y bison yum install -y perl-ExtUt…

字符串轉換整形數,結構體數組排序

#include <stdio.h> #include<string.h> #include<conio.h> #include <stdlib.h> FILE *p; #define N 3 struct hacker {char name[11];char num[20];char xingbie[3]; }z[N];//輸入模塊 void shuru() {char *s"網名 QQ號碼 性別信息…

如何發布打包并發布自己的Android應用(APP)

第一步&#xff0c;在Eclipse中選擇需要打包的項目&#xff0c;然后右鍵--選擇Export&#xff0c;會彈出一個打包的提示框&#xff0c;如下圖所示。 按Next之后&#xff0c;會繼續出現一個提示框&#xff0c;這里你可以選擇自己需要打包的項目&#xff08;默認是剛才選中的&…

js變量提升_一道JS變量提升題

var a 0;if(true){a 1;function a(){};a 21;console.log(a);}console.log(a);// 21 1 當前上下文代碼執行之前&#xff0c;會將帶var/function的進行聲明/定義。當遇到“{}”時&#xff0c;新版瀏覽器和老版瀏覽器的處理不一致。老版瀏覽器&#xff08;IE10以下&#xff09;…

Adaboost的幾個人臉檢測網站

【1】基礎學習筆記之opencv(1)&#xff1a;opencv中facedetect例子淺析 http://www.cnblogs.com/tornadomeet/archive/2012/03/22/2411318.html【2】OpenCV學習筆記&#xff08;二十七&#xff09;——基于級聯分類器的目標檢測objdect http://blog.csdn.net/yang_xian521/arti…

Caffe訓練過程:test_iter test_interval等概念

轉載自http://blog.csdn.net/iamzhangzhuping/article/details/49993899 先上一張圖&#xff0c;大家很熟悉的一張圖。 首先說明一個概念&#xff1a;在caffe中的一次迭代iteration指的是一個batch&#xff0c;而不是一張圖片。 下面主要說下2個概念&#xff1a; test_ite…

R的獲取和安裝

R的獲取和安裝 一、下載 R可以在CRAN&#xff08;Comprehensive r archive network&#xff09;http://cran.r-project.org上免費下載&#xff0c;可供選擇的有Linux、Mac OS X和windows對應的二進制文件&#xff1b; 我這里選擇的是windows版本。打開如下頁面&#xff1a; bas…

Bitmap recycle()

Bitmap調用recycle? When?Bitmap有一個recycle方法。含義很easy&#xff0c;恢復Bitmap空間。 Q 1: Bitmap有調用recycle方法的必要性&#xff1f; A: 嵌入式系統總是格外注重空間的問題&#xff0c;不小心的話就會有OOM。可是應用層使用java的android平臺有其天然的優勢【ja…

擴展歐幾里得算法求逆元_從輾轉相除法到求逆元,數論算法初體驗

今天是算法和數據結構專題的第22篇文章&#xff0c;我們一起來聊聊輾轉相除法。輾轉相除法又名歐幾里得算法&#xff0c;是求最大公約數的一種算法&#xff0c;英文縮寫是gcd。所以如果你在大牛的代碼或者是書上看到gcd&#xff0c;要注意&#xff0c;這不是某某黨&#xff0c;…

[翻譯] Fast Image Cache

https://github.com/path/FastImageCache Fast Image Cache is an efficient, persistent, and—above all—fast way to store and retrieve images in your iOS application. Part of any good iOS applications user experience is fast, smooth scrolling, and Fast Image …

centos 安裝 MatConvNet (gpu)

1. 安裝準備 matlab2017a &#xff0c;參考&#xff1a;《centos 安裝matlab2017a(無root權限)》 GCC 4.8(支持c11) 鍵入&#xff1a;sudo yum install gcc gcc-c &#xff08;建議sudo裝&#xff09; 至少CUDA 7.5&#xff0c;&#xff08;本人選擇cuda8.0&#xff…

php練習 租房子

題目要求 1.封裝類 <?php class DBDA {public $fuwuqi"localhost"; //服務器地址public $yonghuming"root";//用戶名public $mima"";//密碼 public $dbconnect;//連接對象//操作數據庫的方法//$sql代表需要執行的SQL語句//$type代表SQL語…

【SHARE】WEB前端學習資料

參考資料&#xff1a;https://github.com/karlhorky/learn-to-program學習網站&#xff1a;http://www.codecademy.com/learn https://www.codeschool.com/ 制作網站&#xff1a;https://webmaker.org/zh-CN/explore JavaScript2015&#xff1a;https://esdiscuss.org/topic/ja…

python軟件安裝和使用方法_aws cli的安裝及使用(內含python的安裝方法)

liunx環境(使用bundled installer)&#xff1a;1.wget https://s3.amazonaws.com/aws-cli/awscli-bundle.zip //下載bundled installer2.unzip awscli-bundle.zip3.sudo ./awscli-bundle/install -i /usr/local/aws -b /usr/local/bin/aws如果你沒有sudo權限或者是你想在當…

centos 安裝boost(caffe需要)

安裝 由于安裝caffe&#xff0c;要求boost的版本在1.55以上&#xff0c;而服務器上的剛好是1.54,所以進行了重裝。 參考&#xff1a;《CentOS 7下編譯安裝Boost_1_57_0 》 不過由于pycaffe需要boost.python,因此需要在./b2時修改為./b2 –stage debug 才可以。而不能去掉py…

JAVA正則表達式介紹和使用

本文引用自 http://www.cnblogs.com/android-html5/archive/2012/06/02/2533924.html 技術博客 1.Java中在某個字符串中查詢某個字符或者某個子字串 Java代碼 String s "Shang Hai Hong Qiao Fei Ji Chang";    String regEx "a|F"; //表示a或F Pat…

集合框架中的接口及其實現類

Collection&#xff1a;集合層次中的根接口&#xff0c;JDK沒有提供這個接口直接地實現類。Set&#xff1a;不能包含重復的元素。SortedSet是一個按照升序排列元素的Set。List&#xff1a;是一個有序的集合&#xff0c;可以包含重復的元素。提供了按索引訪問的方式。Map&#x…

C# 多線程 Parallel.For 和 For 誰的效率高?那么 Parallel.ForEach 和 ForEach 呢?

還是那句話&#xff1a;十年河東&#xff0c;十年河西&#xff0c;莫欺少年窮。 今天和大家探討一個問題&#xff1a;Parallel.For 和 For 誰的效率高呢&#xff1f; 從CPU使用方面而言&#xff0c;Parallel.For 屬于多線程范疇&#xff0c;可以開辟多個線程使用CPU內核&#x…

cuda、cudnn相關問題鏈接

1. cuda&#xff0c;cudnn安裝 <caffe安裝系列——安裝cuda和cudnn> 2. 查看已有的cuda等版本 cuda 版本 cat /usr/local/cuda/version.txtcudnn 版本 cat /usr/local/cuda/include/cudnn.h | grep CUDNN_MAJOR -A 23. cudnn的安裝&#xff0c;路徑和版本問題 http://…