Node.js中的垃圾回收機制是什么

這篇“Node.js中的垃圾回收機制是什么”文章的知識點大部分人都不太理解,所以小編給大家總結(jié)了以下內(nèi)容,內(nèi)容詳細(xì),步驟清晰,具有一定的借鑒價值,希望大家閱讀完這篇文章能有所收獲,下面我們一起來看看這篇“Node.js中的垃圾回收機制是什么”文章吧。

創(chuàng)新互聯(lián)是一家集網(wǎng)站建設(shè),婁星企業(yè)網(wǎng)站建設(shè),婁星品牌網(wǎng)站建設(shè),網(wǎng)站定制,婁星網(wǎng)站建設(shè)報價,網(wǎng)絡(luò)營銷,網(wǎng)絡(luò)優(yōu)化,婁星網(wǎng)站推廣為一體的創(chuàng)新建站企業(yè),幫助傳統(tǒng)企業(yè)提升企業(yè)形象加強企業(yè)競爭力。可充分滿足這一群體相比中小企業(yè)更為豐富、高端、多元的互聯(lián)網(wǎng)需求。同時我們時刻保持專業(yè)、時尚、前沿,時刻以成就客戶成長自我,堅持不斷學(xué)習(xí)、思考、沉淀、凈化自己,讓我們?yōu)楦嗟钠髽I(yè)打造出實用型網(wǎng)站。

GC,Garbage Collection,垃圾回收。在編程中,一般指的是內(nèi)存自動回收機制,會定時將不需要用到的數(shù)據(jù)進(jìn)行清除。

Node.js 底層使用了 V8 引擎。V8 是 Google 開源的一款高性能 JavaScript 引擎,使用了 C++ 進(jìn)行編寫。

Node.js 的內(nèi)存主要分成三部分:

  • 代碼空間:存放代碼段的地方;

  • 棧:函數(shù)調(diào)用棧產(chǎn)生的臨時變量,為一些基本類型,比如數(shù)字、字符串、布爾值,以及對象引用(保存的是地址,不保存對象本身)。

  • 堆:存放對象等數(shù)據(jù)。

堆內(nèi)存

Node.js 底層使用的是 V8,下面講解一下 V8 的內(nèi)存回收機制。

首先 JS 中所有的對象都會保存在堆內(nèi)存中。在創(chuàng)建進(jìn)程的時候,會分配一個初始大小的堆內(nèi)存,然后我們的對象就會放到里面。

當(dāng)對象越來越多,堆內(nèi)存會不夠用,此時堆內(nèi)存會動態(tài)地擴大。如果到達(dá)一個最大限制(現(xiàn)在通常是 4GB),就會堆內(nèi)存溢出的錯誤,然后終止 Node.js 進(jìn)程。

新生代與老生代

V8 首先將內(nèi)存分成兩部分,或者說兩個生代(generation):

  • 新生代(yong generation):保存一些存活時間較短的對象;

  • 老生代(old generation):保存存活時間長或者長駐的對象。

新生代很小,這里會存放一些存活時間很短的對象,通常它們會被頻繁地回收(比如函數(shù)的調(diào)用棧的一些臨時對象)。

新生代可通過 node --max-semi-space-size=SIZE index.js 修改新生代的大小,單位為 MB。

另外,老生代則通過 --max-old-space-size=SIZE 來設(shè)置

新生代的 Scavenge 算法

新生代使用了 Scavenge 算法,是一種基于 copy(復(fù)制)的算法。

新生代會分成兩個空間,這種空間稱為 semispace,它們?yōu)椋?/p>

  • From 空間:新聲明的對象會放入這里

  • To 空間:用作搬移的空間

新聲明的對象會放入到 From 空間中,F(xiàn)rom 空間的對象緊密排布,通過指針,上一對象緊貼下一個對象,是內(nèi)存連續(xù)的,不用擔(dān)心內(nèi)存碎片的問題。

所謂內(nèi)存碎片,指的是空間分配不均勻,產(chǎn)生大量小的連續(xù)空間,無法放入一個大對象。

當(dāng) From 空間快滿了,我們就會遍歷找出活躍對象,將它們 copy 到 To 空間。此時 From 空間其實就空了,然后我們將 From 和 To 互換身份。

如果一些對象被 copy 了多次,會被認(rèn)為存活時間較長,將被移動到老生代中。

這種基于 copy 的算法,優(yōu)點是可以很好地處理內(nèi)存碎片的問題,缺點是會浪費一些空間作為搬移的空間位置,此外因為拷貝比較耗費時間,所以不適合分配太大的內(nèi)存空間,更多是做一種輔助 GC。

Mark-Sweep 和 Mark-Compact

老生代的空間就比新生代要大得多了,放的是一些存活時間長的對象,用的是 Mark-Sweep (標(biāo)記清除)算法。

首先是標(biāo)記階段。從根集 Root Set(執(zhí)行棧和全局對象)往上找到所有能訪問到的對象,給它們標(biāo)記為活躍對象。

標(biāo)記完后,就是清除階段,將沒有標(biāo)記的對象清除,其實就是標(biāo)記一下這個內(nèi)存地址為空閑。

這種做法會導(dǎo)致 空閑內(nèi)存空間碎片化,當(dāng)我們創(chuàng)建了一個大的連續(xù)對象,就會找不到地方放下。這時候,就要用 Mark-Compact(標(biāo)記整理)來將碎片的活躍對象做一個整合。

Mark-Compact 會將所有活躍對象拷貝移動到一端,然后邊界的另一邊就是一整塊的連續(xù)可用內(nèi)存了。

考慮到 Mark-Sweep 和 Mark-Compact 花費的時間很長,且會阻塞 JavaScript 的線程,所以通常我們不會一次性做完,而是用 增量標(biāo)記 (Incremental Marking)的方式。也就是做斷斷續(xù)續(xù)地標(biāo)記,小步走,垃圾回收和應(yīng)用邏輯交替進(jìn)行。

另外,V8 還做了并行標(biāo)記和并行清理,提高執(zhí)行效率。

Node.js中的垃圾回收機制是什么

查看內(nèi)存相關(guān)信息

我們可以通過 process.memoryUsage 方法拿到內(nèi)存相關(guān)的一些信息。

process.memoryUsage();

輸出內(nèi)容為:

{
  rss: 35454976,
  heapTotal: 7127040,
  heapUsed: 5287088,
  external: 958852,
  arrayBuffers: 11314
}

說明

  • rss:常駐內(nèi)存大?。╮esident set size),包括代碼片段、堆內(nèi)存、棧等部分。

  • heapTotal:V8 的堆內(nèi)存總大??;

  • heapUsed:占用的堆內(nèi)存;

  • external:V8 之外的的內(nèi)存大小,指的是 C++ 對象占用的內(nèi)存,比如 Buffer 數(shù)據(jù)。

  • arrayBuffers:ArrayBufferSharedArrayBuffer 相關(guān)的內(nèi)存大小,屬于 external 的一部分。

以上數(shù)字的單位都是字節(jié)。

測試最大內(nèi)存限制

寫一個腳本,用一個定時器,讓一個數(shù)組不停地變大,并打印堆內(nèi)存使用情況,直到內(nèi)存溢出。

const format = function (bytes) {
  return (bytes / 1024 / 1024).toFixed(2) + " MB";
};

const printMemoryUsage = function () {
  const memoryUsage = process.memoryUsage();
  console.log(
    `heapTotal: ${format(memoryUsage.heapTotal)}, heapUsed: ${format(
      memoryUsage.heapUsed
    )}`
  );
};

const bigArray = [];
setInterval(function () {
  bigArray.push(new Array(20 * 1024 * 1024));
  printMemoryUsage();
}, 500);

需要特別注意的是,不要用 Buffer 做測試。

因為 Buffer 是 Node.js 特有的處理二進(jìn)制的對象,它不是在 V8 中的實現(xiàn)的,是 Node.js 用 C++ 另外實現(xiàn)的,不通過 V8 分配內(nèi)存,屬于堆外內(nèi)存。

我使用電腦是 macbook pro M1 Pro,Node.js 版本為 v16.17.0,使用的 V8 版本是 9.4.146.26-node.22(通過 process.versions.v8 得到)。

輸出結(jié)果為(省略了一些多余的信息):

heapTotal: 164.81 MB, heapUsed: 163.93 MB
heapTotal: 325.83 MB, heapUsed: 323.79 MB
heapTotal: 488.59 MB, heapUsed: 483.84 MB
...
heapTotal: 4036.44 MB, heapUsed: 4003.37 MB
heapTotal: 4196.45 MB, heapUsed: 4163.29 MB

<--- Last few GCs --->

[28033:0x140008000]    17968 ms: Mark-sweep 4003.2 (4036.4) -> 4003.1 (4036.4) MB, 2233.8 / 0.0 ms  (average mu = 0.565, current mu = 0.310) allocation failure scavenge might not succeed
[28033:0x140008000]    19815 ms: Mark-sweep 4163.3 (4196.5) -> 4163.1 (4196.5) MB, 1780.3 / 0.0 ms  (average mu = 0.413, current mu = 0.036) allocation failure scavenge might not succeed


<--- JS stacktrace --->

FATAL ERROR: Reached heap limit Allocation failed - JavaScript heap out of memory
...

可以看到,是在 4000 MB 之后超出了內(nèi)存上限,發(fā)生堆溢出,然后退出了進(jìn)程。說明在我的機器上,默認(rèn)的最大內(nèi)存為 4G。

實際最大內(nèi)存和它運行所在的機器有關(guān),如果你的機器的內(nèi)存大小為 2G,最大內(nèi)存將設(shè)置為 1.5G。

以上就是關(guān)于“Node.js中的垃圾回收機制是什么”這篇文章的內(nèi)容,相信大家都有了一定的了解,希望小編分享的內(nèi)容對大家有幫助,若想了解更多相關(guān)的知識內(nèi)容,請關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道。

名稱欄目:Node.js中的垃圾回收機制是什么
本文地址:http://bm7419.com/article18/jcsidp.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站策劃、搜索引擎優(yōu)化、面包屑導(dǎo)航靜態(tài)網(wǎng)站、電子商務(wù)建站公司

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

綿陽服務(wù)器托管