您好,登錄后才能下訂單哦!
這篇文章主要為大家展示了“Node.js中GC機制的示例分析”,內容簡而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領大家一起研究并學習一下“Node.js中GC機制的示例分析”這篇文章吧。
V8 的內存限制
在一般的后端開發(fā)語言中,在基本的內存使用上沒有什么限制,然而在 Node 中通過 JavaScript 使用內存時就會發(fā)現(xiàn)只能使用部分內存(64位系統(tǒng)下約為1.4GB,32位系統(tǒng)下約為0.7GB)。在這樣的限制下,將會導致 Node 無法直接操作大內存對象。
造成這個問題的主要原因在于 Node 的 JavaScript 執(zhí)行引擎 V8。
在 V8 中,所有的 JavaScript 對象都是通過堆來進行分配的。Node 提供了 V8 中內存的使用量查看方法
process.memoryUsage()。
heapTotal 已申請到的堆內存
heapUsed 當前使用的堆內存
為什么 V8 要限制堆的大小:
1.V8 為瀏覽器而設計,不太可能遇到用大量內存的場景
2.V8 的垃圾回收機制的限制。(按官方的說法,以1.5GB的垃圾回收堆內存為例,V8做一次小的垃圾回收需要50ms以上,做一次非增量式的垃圾回收需要1s以上)
V8提供了選項讓我們可以控制使用內存的大小
node --max-old-space-size=1700 test.js
設置老生代內存空間最大值,單位為MB
node --max-new-space-size=1024 test.js
設置新生代內存空間最大值,單位為KB
比較遺憾的是,這兩個最大值需要在啟動時執(zhí)行。這意味著 V8 使用的內存沒辦法根據(jù)使用的情況自動擴充,當內存分配過程中超過極限值時,就會引起進程出錯。
V8 的垃圾回收機制
V8 的垃圾回收策略主要基于分代式垃圾回收機制。在 V8 中,主要將內存分為新生代和老生代兩代。新生代中的對象為存活時間較短的對象,老生代中的對象為存活時間較長或常駐內存的對象。
V8 堆的整體大小就是新生代的內存空間加上老生代的內存空間
Scavenge 算法
在分代的基礎上,新生代中的對象主要通過 Scavenge 算法進行垃圾回收。在 Scavenge 的具體實現(xiàn)中,主要采用了 Cheney 算法。
Cheney 算法是一種采用復制的方式實現(xiàn)的垃圾回收算法。它將堆內存一分為二,每一部分空間成為 semispace。在這兩個 semispace 空間中,只有一個處于使用中,另一個處于閑置中。處于使用中的 semispace 空間成為 From 空間,處于閑置狀態(tài)的空間成為 To 空間。當我們分配對象時,先是在 From 空間中進行分配。當開始進行垃圾回收時,會檢查 From 空間中的存活對象,這些存活對象將被復制到 To 空間中,而非存活對象占用的空間將被釋放。完成復制后, From 空間和 To 空間的角色發(fā)生對換。
Scavenge 的缺點是只能使用堆內存的一半,但 Scavenge 由于只復制存活的對象,并且對于生命周期短的場景存活對象只占少部分,所以它在時間效率上表現(xiàn)優(yōu)異。Scavenge 是典型的犧牲空間換取時間的算法,無法大規(guī)模地應用到所有的垃圾回收中,但非常適合應用在新生代中。
晉升
對象從新生代中移動到老生代中的過程稱為晉升。
From 空間中的存活對象在復制到 To 空間之前需要進行檢查,在一定條件下,需要將存活周期長的對象移動到老生代中,也就是完成對象的晉升。
晉升條件主要有兩個:
1.對象是否經歷過一次 Scavenge 回收
2.To 空間已經使用超過 25%
設置 25% 這個限制值得原因是當這次 Scavenge 回收完成后,這個 To 空間將變成 From 空間,接下來的內存分配將在這個空間中進行,如果占比過高,會影響后續(xù)的內存分配。
Mark-Sweep & Mark-Compact
V8 在老生代中主要采用了 Mark-Sweep 和 Mark-Compact 相結合的方式進行垃圾回收。
Mark-Sweep 是標記清除的意思,它分為兩個階段,標記和清除。Mark-Sweep 在標記階段遍歷堆中的所有對象,并標記活著的對象,在隨后的清除階段中,只清除未被標記的對象。
Mark-Sweep 最大的問題是在進行一次標記清除回收后,內存空間會出現(xiàn)不連續(xù)的狀態(tài)。這種內存碎片會對后續(xù)的內存分配造成問題,因為很可能出現(xiàn)需要分配一個大對象的情況,這時所有的碎片空間都無法完成此次分配,就會提前觸發(fā)垃圾回收,而這次回收是不必要的。
為了解決 Mark-Sweep 的內存碎片問題,Mark-Compact 被提出來。Mark-Compact是標記整理的意思,是在 Mark-Sweep 的基礎上演進而來的。它們的差別在于對象在標記為死亡后,在整理過程中,將活著的對象往一端移動,移動完成后,直接清理掉邊界外的內存。
下表為3種主要垃圾回收算法的簡單比較
從表中可以看出,在 Mark-Sweep 和 Mark-Compact 之間,由于 Mark-Compact 需要移動對象,所以它的執(zhí)行速度不可能很快,所以在取舍上,V8 主要使用 Mark-Sweep,在空間不足以從新生代中晉升過來的對象進行分配時才使用 Mark-Compact 。
Incremental Marking
為了避免出現(xiàn) JavaScript 應用邏輯與垃圾回收器看到的不一致的情況,垃圾回收的3種算法都需要將應用邏輯暫停下來,這種行為稱為“全停頓” (stop-the-world)。
由于新生代配置的空間較小,存活對象較少,全停頓對新生代影響不大。但老生代通常配置的空間較大,且存活對象較多,全堆垃圾回收(full 垃圾回收)的標記、清除、整理等動作造成的停頓就會比較可怕。
為了降低全堆垃圾回收帶來的停頓時間,V8 先從標記階段入手,將原本要一口氣停頓完成的動作改成增量標記(Incremental Marking),也就是拆分為許多小“步進”,每做完一“步進”就讓JavaScript應用邏輯執(zhí)行一小會兒,垃圾回收和應用邏輯交替執(zhí)行直到標記階段完成。
V8 在經過增量標記的改進后,垃圾回收的最大停頓時間可以減少到原本的 1/6 左右。
查看GC日志
查看垃圾回收日志的方式主要是在啟動時添加 --trace_gc 參數(shù)。
以上是“Node.js中GC機制的示例分析”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業(yè)資訊頻道!
免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經查實,將立刻刪除涉嫌侵權內容。