V8 內存管理及垃圾回收機制

因爲 V8 引擎的緣由,Node 在操做大內存對象時受到了一些限制,在 64 位的機器上,默認最大操做的對象大小約爲 1.4G,在 32 位的機器上,默認最大操做的對象大小約爲 0.7G。
若是咱們的 Node 程序會常常操做一些大內存的對象,能夠對這個默認值進行修改:node

node --max-old-space-size=1700 index.js
node --max-new-space-size=1024 index.js

其中,max-old-space-size 表示設置老生代內存空間的最大容量,max-new-space-size 表示這隻新生代內存空間的最大容量。但這兩個值也是有上限的,不能無限設置,其中老生代內存空間最大的值約爲 1.7G,新生代最大內存空間約爲 1.0G。
至於新生代和老生代,這是 V8 對內存的一個分類,後文再介紹。
回到操做大內存對象的問題,若是 1.7G 的內存仍是不夠大怎麼辦呢?要知道 1.7G 是在 V8 引擎層面上作出的限制,要想避開這種限制,咱們可使用 Buffer 對象,Buffer 對象的內存分配在 C++ 層面進行,不受 V8 引擎限制。
注:經過 process.memoryUsage() 方法能夠用來查看 V8 引擎的內存使用量,經過 os.totalmem() 方法和 os.freemem() 方法分別能夠查看操做系統的總內存和空閒內存。web

// 查看 V8 的內存使用狀況
process.memoryUsage()
{ 
  rss: 31469568,
  heapTotal: 7708672,
  heapUsed: 5152856,
  external: 8609 
}

// 查看操做系統總內存
os.totalmem()
8279511040
// 查看操做系統的空閒內存
os.freemem()
1610977280

經過上面幾個方法獲取到的內存都是以字節爲單位。算法

新生代和老生代

V8 將內存分爲兩類:新生代內存空間和老生代內存空間,新生代內存空間主要用來存放存活時間較短的對象,老生代內存空間主要用來存放存活時間較長的對象。對於垃圾回收,新生代和老生代有各自不一樣的策略,下面依次進行介紹。swift

新生代垃圾回收

新生代內存中的垃圾回收主要經過 Scavenge 算法進行,具體實現時主要採用了 Cheney 算法。Cheney 將內存空間一分爲二,每部分都叫作一個 Semispace,這兩個 Semispace 一個處於使用,一個處於閒置。處於使用中的 Semispace 也叫做 From,處於閒置中的 Semispace 也叫做 To。
在垃圾回收運行時時,會檢查 From 中的對象,當某個對象須要被回收時,將其留在 From 空間,剩下的對象移動到 To 空間,而後進行反轉,將 From 空間和 To 空間互換。進行垃圾回收時,會將 To 空間的內存進行釋放,以下圖所示:spa

新生代垃圾回收.png操作系統

簡而言之,就是 From 空間中最終存放不須要被回收的對象,To 空間最終中存放須要被回收的對象,當垃圾回收運行時,將 To 空間中的對象所有進行回收。code

新生代對象的晉升

前面說過,新生代內存空間用來存放存活時間較短的對象,老生代內存空間用來存放存活時間較長的對象。新生代中的對象能夠晉升到老生代中,具體有兩種方式:orm

1. 屢次聲明晉升

在垃圾回收的過程當中,若是發現某個對象以前被清理過,那麼會將其晉升到老生代內存空間中對象

2. 佔用大內存晉升

在 From 空間和 To 空間進行反轉的過程當中,若是 To 空間中(保留的數據)的使用量已經超過了 25%,那麼就將 From 中的對象直接晉升到老生代內存空間中ip

老生代垃圾回收

說完新生代的垃圾回收,再來看下老生代中的垃圾回收。首先,老生代內存空間和新生代內存空間的結構不同,實際上是一個連續的結構,而不像新生代內存空間那樣分爲 From 和 To 兩個部分:

老生代內存空間.png

老生代內存空間中的垃圾回收有標記清除(Mark Sweep)和標記合併(Mark Compact)兩種方式。

Mark Sweep

Mark Sweep 是將須要被回收的對象進行標記,在垃圾回收運行時直接釋放相應的地址空間,以下圖所示(紅色的內存區域表示須要被回收的區域):

標記清除.png

如上圖所示,使用 Mark Sweep 進行垃圾回收會產生一個問題,就是垃圾回收後內存會出現不連續的狀況,爲了解決這個問題,出現了 Mark Compact 方案。

Mark Compact

Mark Compact 的思想有點像新生代垃圾回收時採起的 Cheney 算法:將存活的對象移動到一邊,將須要被回收的對象移動到另外一邊,而後對須要被回收的對象區域進行總體的垃圾回收。

標記合併.png

上圖展現了在老生代內存空間使用 Mark Compact 進行垃圾回收的過程。

總結

本文主要簡單介紹了 Node 的內存管理和垃圾回收相關的知識:

  • V8 的大對象操做限制問題和解決方案
  • 獲取內存使用量的幾個方法
  • 新生代和老生代的概念
  • 新生代和老生代的垃圾回收方案
相關文章
相關標籤/搜索