真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

Node中堆內(nèi)存分配的示例分析

這篇文章將為大家詳細(xì)講解有關(guān)Node中堆內(nèi)存分配的示例分析,小編覺(jué)得挺實(shí)用的,因此分享給大家做個(gè)參考,希望大家閱讀完這篇文章后可以有所收獲。

讓客戶滿意是我們工作的目標(biāo),不斷超越客戶的期望值來(lái)自于我們對(duì)這個(gè)行業(yè)的熱愛。我們立志把好的技術(shù)通過(guò)有效、簡(jiǎn)單的方式提供給客戶,將通過(guò)不懈努力成為客戶在信息化領(lǐng)域值得信任、有價(jià)值的長(zhǎng)期合作伙伴,公司提供的服務(wù)項(xiàng)目有:主機(jī)域名、虛擬空間、營(yíng)銷軟件、網(wǎng)站建設(shè)、洪雅網(wǎng)站維護(hù)、網(wǎng)站推廣。

V8 垃圾回收簡(jiǎn)介

首先,簡(jiǎn)單介紹一下V8垃圾回收器。內(nèi)存的存儲(chǔ)分配方式是堆(heap),堆被分為幾個(gè)世代(generational)區(qū)域。 對(duì)象在它的生命周期中隨著年齡的變化,它所屬的世代也有所不同。

世代中分為年輕一代和老一代,而年輕的一代還分為了新生代和中間代。隨著對(duì)象在垃圾回收中幸存下來(lái),它們也會(huì)加入老一代。

Node中堆內(nèi)存分配的示例分析

世代假說(shuō)的基本原則是大多數(shù)對(duì)象都是年輕的。V8 垃圾回收器基于這一點(diǎn),只提升在垃圾回收中幸存下來(lái)的對(duì)象。隨著對(duì)象被復(fù)制到相鄰區(qū)域,它們最終會(huì)進(jìn)入老一代。

在Nodejs中內(nèi)存消耗主要分為三個(gè)方面:

  • 代碼-代碼執(zhí)行時(shí)所在的位置

  • 調(diào)用棧-用于存放具有原始類型(數(shù)字,字符串或布爾值)的函數(shù)和局部變量

  • 堆內(nèi)存

堆內(nèi)存是我們今天的主要關(guān)注點(diǎn)。 現(xiàn)在您對(duì)垃圾回收器有了更多的了解,是時(shí)候在堆上分配一些內(nèi)存了!

function allocateMemory(size) {
  // Simulate allocation of bytes
  const numbers = size / 8;
  const arr = [];
  arr.length = numbers;
  for (let i = 0; i < numbers; i++) {
    arr[i] = i;
  }
  return arr;
}

在調(diào)用棧中,局部變量隨著函數(shù)調(diào)用結(jié)束而銷毀?;A(chǔ)類型 number永遠(yuǎn)不會(huì)進(jìn)入堆內(nèi)存,而是在調(diào)用棧中分配。但是對(duì)象arr將進(jìn)入堆中并且可能在垃圾回收中幸存下來(lái)。

堆內(nèi)存有限制嗎?

現(xiàn)在進(jìn)行勇敢測(cè)試——將 Node 進(jìn)程推到極限看看在哪個(gè)地方會(huì)耗盡堆內(nèi)存:

const memoryLeakAllocations = [];

const field = "heapUsed";
const allocationStep = 10000 * 1024; // 10MB

const TIME_INTERVAL_IN_MSEC = 40;

setInterval(() => {
  const allocation = allocateMemory(allocationStep);

  memoryLeakAllocations.push(allocation);

  const mu = process.memoryUsage();
  // # bytes / KB / MB / GB
  const gbNow = mu[field] / 1024 / 1024 / 1024;
  const gbRounded = Math.round(gbNow * 100) / 100;

  console.log(`Heap allocated ${gbRounded} GB`);
}, TIME_INTERVAL_IN_MSEC);

在上面的代碼中,我們以 40 毫秒的間隔分配了大約 10 mb,為垃圾回收提供了足夠的時(shí)間來(lái)將幸存的對(duì)象提升到老年代。process.memoryUsage 是一個(gè)用于回收有關(guān)堆利用率的粗略指標(biāo)的工具。隨著堆分配的增長(zhǎng),heapUsed 字段會(huì)記錄堆的大小。這個(gè)字段記錄 RAM 中的字節(jié)數(shù),可以轉(zhuǎn)換為mb。

你的結(jié)果可能會(huì)有所不同。在32GB 內(nèi)存的 Windows 10 筆記本電腦會(huì)得到以下結(jié)果:

Heap allocated 4 GB
Heap allocated 4.01 GB

<--- Last few GCs --->

[18820:000001A45B4680A0] 26146 ms: Mark-sweep (reduce) 4103.7 (4107.3) -> 4103.7 (4108.3) MB, 1196.5 / 0.0 ms (average mu = 0.112, current mu = 0.000) last resort GC in old space requested

<--- JS stacktrace --->

FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory

在這里,垃圾回收器將嘗試壓縮內(nèi)存作為最后的手段,最后放棄并拋出“堆內(nèi)存不足”異常。這個(gè)過(guò)程達(dá)到了 4.1GB 的限制,需要 26.6 秒才能意識(shí)到要把服務(wù)給掛掉了。

導(dǎo)致以上結(jié)果的原因有些還未知。V8 垃圾回收器最初運(yùn)行在具有嚴(yán)格內(nèi)存限制的 32 位瀏覽器進(jìn)程中。這些結(jié)果表明內(nèi)存限制可能已經(jīng)從遺留代碼中繼承下來(lái)。

在撰寫本文時(shí),以上代碼在最新的 LTS Node 版本下運(yùn)行,并且使用的是 64 位可執(zhí)行文件。從理論上講,一個(gè) 64 位進(jìn)程應(yīng)該能夠分配超過(guò) 4GB 的空間,并且可以輕松地增長(zhǎng)到 16 TB 的地址空間。

擴(kuò)大內(nèi)存分配限制

node index.js --max-old-space-size=8000

這將最大限制設(shè)置為 8GB。這樣做時(shí)要小心。我的筆記本電腦有 32GB的空間。我建議將其設(shè)置為 RAM 中實(shí)際可用的空間。一旦物理內(nèi)存耗盡,進(jìn)程就會(huì)開始通過(guò)虛擬內(nèi)存占用磁盤空間。如果您將限制設(shè)置得太高,你就get了換電腦的新理由,這里咱們盡量避免電腦冒煙了哈~

我們?cè)儆?GB的限制再跑一次代碼:

Heap allocated 7.8 GB
Heap allocated 7.81 GB

<--- Last few GCs --->

[16976:000001ACB8FEB330] 45701 ms: Mark-sweep (reduce) 8000.2 (8005.3) -> 8000.2 (8006.3) MB, 1468.4 / 0.0 ms (average mu = 0.211, current mu = 0.000) last resort GC in old space requested

<--- JS stacktrace --->

FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory

這一次堆的大小幾乎達(dá)到 8GB,但沒(méi)完全達(dá)到。我懷疑是Node 進(jìn)程中有一些開銷用于分配這么多內(nèi)存。這次進(jìn)程結(jié)束需要 45.7 秒。

在生產(chǎn)環(huán)境中,內(nèi)存全部用完可能不會(huì)少于一分鐘。這就是監(jiān)控和洞察內(nèi)存消耗有幫助的原因之一。內(nèi)存消耗會(huì)隨著時(shí)間的推移緩慢增長(zhǎng),并且可能需要幾天時(shí)間才能知道存在問(wèn)題。如果進(jìn)程不斷崩潰并且日志中出現(xiàn)“堆內(nèi)存不足”異常,則代碼中可能存在內(nèi)存泄漏。

進(jìn)程也可能會(huì)占用更多內(nèi)存,因?yàn)樗谔幚砀鄶?shù)據(jù)。如果資源消耗繼續(xù)增長(zhǎng),可能是時(shí)候?qū)⑦@個(gè)單體分解為微服務(wù)了。這將減少單個(gè)進(jìn)程的內(nèi)存壓力,并允許節(jié)點(diǎn)水平擴(kuò)展。

如何跟蹤 Node.js 內(nèi)存泄漏

process.memoryUsage 的 heapUsed  字段還是有點(diǎn)用的,調(diào)試內(nèi)存泄漏的一個(gè)方法是將內(nèi)存指標(biāo)放在另一個(gè)工具中以進(jìn)行進(jìn)一步處理。由于此實(shí)現(xiàn)并不復(fù)雜,因此主要解析下如何親自實(shí)現(xiàn)。

const path = require("path");
const fs = require("fs");
const os = require("os");

const start = Date.now();
const LOG_FILE = path.join(__dirname, "memory-usage.csv");

fs.writeFile(LOG_FILE, "Time Alive (secs),Memory GB" + os.EOL, () => {}); // 請(qǐng)求-確認(rèn)

為了避免將堆分配指標(biāo)放在內(nèi)存中,我們選擇將結(jié)果寫入 CSV 文件以方便數(shù)據(jù)消耗。這里使用了 writeFile 帶有回調(diào)的異步函數(shù)?;卣{(diào)為空以寫入文件并繼續(xù),無(wú)需任何進(jìn)一步處理。 要獲取漸進(jìn)式內(nèi)存指標(biāo),請(qǐng)將其添加到 console.log:

const elapsedTimeInSecs = (Date.now() - start) / 1000;
const timeRounded = Math.round(elapsedTimeInSecs * 100) / 100;

s.appendFile(LOG_FILE, timeRounded + "," + gbRounded + os.EOL, () => {}); // 請(qǐng)求-確認(rèn)

上面這段代碼可以用來(lái)調(diào)試內(nèi)存泄漏的情況下,堆內(nèi)存隨著時(shí)間變化而增長(zhǎng)。你可以使用一些分析工具來(lái)解析原生csv數(shù)據(jù)以實(shí)現(xiàn)一個(gè)比較漂亮的可視化。

如果你只是趕著看看數(shù)據(jù)的情況,直接用excel也可以,如下圖:

Node中堆內(nèi)存分配的示例分析

在限制為4.1GB的情況下,你可以看到內(nèi)存的使用率在短時(shí)間內(nèi)呈線性增長(zhǎng)。內(nèi)存的消耗在持續(xù)的增長(zhǎng)并沒(méi)有變得平緩,這個(gè)說(shuō)明了某個(gè)地方存在內(nèi)存泄漏。在我們調(diào)試這類問(wèn)題的時(shí)候,我們要尋找在分配在老世代結(jié)束時(shí)的那部分代碼。

對(duì)象如果再在垃圾回收時(shí)幸存下來(lái),就可能會(huì)一直存在,直到進(jìn)程終止。

使用這段內(nèi)存泄漏檢測(cè)代碼更具復(fù)用性的一種方法是將其包裝在自己的時(shí)間間隔內(nèi)(因?yàn)樗槐卮嬖谟谥餮h(huán)中)。

setInterval(() => {
  const mu = process.memoryUsage();
  // # bytes / KB / MB / GB
  const gbNow = mu[field] / 1024 / 1024 / 1024;
  const gbRounded = Math.round(gbNow * 100) / 100;

  const elapsedTimeInSecs = (Date.now() - start) / 1000;
  const timeRounded = Math.round(elapsedTimeInSecs * 100) / 100;

  fs.appendFile(LOG_FILE, timeRounded + "," + gbRounded + os.EOL, () => {}); // fire-and-forget
}, TIME_INTERVAL_IN_MSEC);

要注意上面這些方法并不能直接在生產(chǎn)環(huán)境中使用,僅僅只是告訴你如何在本地環(huán)境調(diào)試內(nèi)存泄漏。在實(shí)際實(shí)現(xiàn)時(shí)還包括了自動(dòng)顯示、警報(bào)和輪換日志,這樣服務(wù)器才不會(huì)耗盡磁盤空間。

跟蹤生產(chǎn)環(huán)境中的 Node.js 內(nèi)存泄漏

盡管上面的代碼在生產(chǎn)環(huán)境中不可行,但我們已經(jīng)看到了如何去調(diào)試內(nèi)存泄漏。因此,作為替代方案,可以將 Node 進(jìn)程包裹在 PM2 之類 的 守護(hù)進(jìn)程 中。

當(dāng)內(nèi)存消耗達(dá)到限制時(shí)設(shè)置重啟策略:

pm2 start index.js --max-memory-restart 8G

單位可以是 K(千字節(jié))、M(兆字節(jié))和 G(千兆字節(jié))。進(jìn)程重啟大約需要 30 秒,因此通過(guò)

事件通過(guò)leak觸發(fā),并且它的回調(diào)對(duì)象中有一個(gè)reason會(huì)隨著連續(xù)垃圾回收的堆增長(zhǎng)而增長(zhǎng)。

使用 AppSignal 的 Magic Dashboard 診斷內(nèi)存限制

AppSignal 有一個(gè)神奇的儀表板,用于監(jiān)控堆增長(zhǎng)的垃圾收集統(tǒng)計(jì)信息。

Node中堆內(nèi)存分配的示例分析

上圖顯示請(qǐng)求在 14:25 左右停止了 7 分鐘,允許垃圾回收以減少內(nèi)存壓力。當(dāng)對(duì)象在舊的空間中停留太久并導(dǎo)致內(nèi)存泄漏時(shí),儀表板也會(huì)暴露出來(lái)。

關(guān)于“Node中堆內(nèi)存分配的示例分析”這篇文章就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,使各位可以學(xué)到更多知識(shí),如果覺(jué)得文章不錯(cuò),請(qǐng)把它分享出去讓更多的人看到。


本文標(biāo)題:Node中堆內(nèi)存分配的示例分析
鏈接分享:
http://weahome.cn/article/giosjg.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部