這篇文章主要介紹 V8 的內存管理和垃圾回收知識。
V8 內存管理及垃圾回收機制淺析
由于 V8 引擎的原因,Node 在操作大內存對象時受到了一些限制,在 64 位的機器上,默認最大操作的對象大小約為 1.4G,在 32 位的機器上,默認最大操作的對象大小約為 0.7G。
如果我們的 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() 方法分別可以查看操作系統(tǒng)的總內存和空閑內存。
// 查看 V8 的內存使用情況
process.memoryUsage()
{
rss: 31469568,
heapTotal: 7708672,
heapUsed: 5152856,
external: 8609
}
// 查看操作系統(tǒng)總內存
os.totalmem()
8279511040
// 查看操作系統(tǒng)的空閑內存
os.freemem()
1610977280
通過上面幾個方法獲取到的內存都是以字節(jié)為單位。
新生代和老生代
V8 將內存分為兩類:新生代內存空間和老生代內存空間,新生代內存空間主要用來存放存活時間較短的對象,老生代內存空間主要用來存放存活時間較長的對象。對于垃圾回收,新生代和老生代有各自不同的策略,下面依次進行介紹。
新生代垃圾回收
新生代內存中的垃圾回收主要通過 Scavenge 算法進行,具體實現(xiàn)時主要采用了 Cheney 算法。Cheney 將內存空間一分為二,每部分都叫做一個 Semispace,這兩個 Semispace 一個處于使用,一個處于閑置。處于使用中的 Semispace 也叫作 From,處于閑置中的 Semispace 也叫作 To。
在垃圾回收運行時時,會檢查 From 中的對象,當某個對象需要被回收時,將其留在 From 空間,剩下的對象移動到 To 空間,然后進行反轉,將 From 空間和 To 空間互換。進行垃圾回收時,會將 To 空間的內存進行釋放,如下圖所示:

簡而言之,就是 From 空間中存放不需要被回收的對象,To 空間中存放需要被回收的對象,當垃圾回收運行時,將 To 空間中的對象全部進行回收。
新生代對象的晉升
前面說過,新生代內存空間用來存放存活時間較短的對象,老生代內存空間用來存放存活時間較長的對象。新生代中的對象可以晉升到老生代中,具體有兩種方式:
- 在垃圾回收的過程中,如果發(fā)現(xiàn)某個對象之前被清理過,那么會將其晉升到老生代內存空間中
- 在 From 空間和 To 空間進行反轉的過程中,如果 To 空間中的使用量已經超過了 25%,那么就將 From 中的對象直接晉升到老生代內存空間中
老生代垃圾回收
說完新生代的垃圾回收,再來看下老生代中的垃圾回收。首先,老生代內存空間和新生代內存空間的結構不一樣,其是一個連續(xù)的結構,而不像新生代內存空間那樣分為 From 和 To 兩個部分:

老生代內存空間中的垃圾回收有標記清除(Mark Sweep)和標記合并(Mark Compact)兩種方式。
Mark Sweep
Mark Sweep 是將需要被回收的對象進行標記,在垃圾回收運行時直接釋放相應的地址空間,如下圖所示(紅色的內存區(qū)域表示需要被回收的區(qū)域):

如上圖所示,使用 Mark Sweep 進行垃圾回收會產生一個問題,就是垃圾回收后內存會出現(xiàn)不連續(xù)的情況,為了解決這個問題,出現(xiàn)了 Mark Compact 方案。
Mark Compact
Mark Compact 的思想有點像新生代垃圾回收時采取的 Cheney 算法:將存活的對象移動到一邊,將需要被回收的對象移動到另一邊,然后對需要被回收的對象區(qū)域進行整體的垃圾回收。

上圖展示了在老生代內存空間使用 Mark Compact 進行垃圾回收的過程。
總結
本文主要簡單介紹了 Node 的內存管理和垃圾回收相關的知識:
- V8 的大對象操作限制問題和解決方案
- 獲取內存使用量的幾個方法
- 新生代和老生代的概念
- 新生代和老生代的垃圾回收方案
附:參考資料
V8的垃圾回收機制與內存限制
node 內存限制的問題
node內存控制
完。