如何查看 V8 的内存使用情况:process.memoryUsage()、v8.getHeapStatistics()、Heap Snapshot 怎么选?
面试速答(30 秒版 TL;DR)
- 查看 V8 内存,最常用的不是一种手段,而是三层视角:进程级、V8 堆级、对象级。
process.memoryUsage()适合先看整体趋势:rss、heapTotal、heapUsed、external、arrayBuffers。node:v8模块适合看堆细节:v8.getHeapStatistics()看堆上限与可用空间,v8.getHeapSpaceStatistics()看new_space、old_space等分代空间。- 如果你要回答“到底是谁没被回收”,只看数字不够,要上 Heap Snapshot / DevTools Memory。
- 面试里最容易答错的一句是:
rss不是 V8 堆大小,heapUsed也不是整个进程占用。
先建立心智模型:你到底想看哪一层
先按三层视角记:进程级、V8 堆级、对象级。
面试里可以直接这样组织答案:
- 先用
process.memoryUsage()看趋势。 - 再用
v8模块看堆和分代。 - 真怀疑泄漏,再抓 Heap Snapshot 看引用链。
一、先看整体:process.memoryUsage()
这是最常用的第一步,因为它成本最低,也最适合接监控。
const { memoryUsage } = require('node:process')
setInterval(() => {
const mem = memoryUsage()
console.log({
rssMB: (mem.rss / 1024 / 1024).toFixed(1),
heapTotalMB: (mem.heapTotal / 1024 / 1024).toFixed(1),
heapUsedMB: (mem.heapUsed / 1024 / 1024).toFixed(1),
externalMB: (mem.external / 1024 / 1024).toFixed(1),
arrayBuffersMB: (mem.arrayBuffers / 1024 / 1024).toFixed(1),
})
}, 5000)
这些字段怎么讲
rss:进程当前驻留在物理内存中的总量,包含 V8 堆、C++ 层、代码段、栈等。heapTotal:V8 已经向系统申请到的堆容量。heapUsed:当前真正被 JS 对象占用的堆内存。external:V8 堆外、但由 JS 对象间接持有的外部内存,典型是 C++ 绑定资源。arrayBuffers:ArrayBuffer/SharedArrayBuffer/ NodeBuffer相关内存,这部分也会反映到external。
面试加分点
- 如果
heapUsed稳定,但rss持续涨,问题不一定在 V8 堆,也可能在原生模块、Buffer、碎片化或其他进程级占用。 - 如果
heapUsed持续涨且不回落,才更像是 JS 对象没有被释放。
二、看 V8 堆本身:v8.getHeapStatistics()
当面试官继续追问“默认堆上限怎么看”时,就要切到 node:v8。
const v8 = require('node:v8')
const stats = v8.getHeapStatistics()
console.log({
totalHeapMB: (stats.total_heap_size / 1024 / 1024).toFixed(1),
usedHeapMB: (stats.used_heap_size / 1024 / 1024).toFixed(1),
availableMB: (stats.total_available_size / 1024 / 1024).toFixed(1),
heapLimitMB: (stats.heap_size_limit / 1024 / 1024).toFixed(1),
nativeContexts: stats.number_of_native_contexts,
detachedContexts: stats.number_of_detached_contexts,
})
重点字段怎么答
heap_size_limit:V8 堆的最大上限,默认值由运行时环境决定,也可能被--max-old-space-size影响。total_available_size:离触达堆上限还有多少可用空间。number_of_native_contexts:顶层上下文数量,持续增长通常要警惕泄漏。number_of_detached_contexts:被分离但还未回收的上下文,非 0 往往值得排查。
一个本地示例
在当前仓库这台机器上,我用 Node v24.12.0、arm64 实测:
require('node:v8').getHeapStatistics().heap_size_limit / 1024 / 1024
// 4288
这个值只能当 当前环境示例,不能背成“V8 永远就是 4288MB”。不同 Node / V8 版本、系统位数、启动参数下都可能不同。
三、看分代空间:v8.getHeapSpaceStatistics()
如果你想把“为什么 Minor GC 很频繁、为什么老生代顶满后更危险”讲清楚,就应该看 heap space。
const v8 = require('node:v8')
const spaces = v8.getHeapSpaceStatistics().map((space) => ({
name: space.space_name,
sizeMB: (space.space_size / 1024 / 1024).toFixed(1),
usedMB: (space.space_used_size / 1024 / 1024).toFixed(1),
availableMB: (space.space_available_size / 1024 / 1024).toFixed(1),
}))
console.table(spaces)
常见会看到这些空间:
new_space:新生代,短命对象多,Minor GC 高频发生。old_space:老生代,长寿对象多,回收更重。code_space:JIT 代码相关空间。map_space:隐藏类等元信息空间。large_object_space:大对象空间。
面试口径
- 看整体趋势 用
process.memoryUsage()。 - 看堆上限和上下文 用
getHeapStatistics()。 - 看新生代/老生代是否失衡 用
getHeapSpaceStatistics()。
四、想知道“谁没被回收”:Heap Snapshot 才是终局工具
很多人会说“我看 heapUsed 一直涨,所以是闭包泄漏”。这种判断太粗。
更稳的排查流程是:
- 启动 Node Inspector:
node --inspect app.js
- 打开 Chrome DevTools 连接到 Node 进程。
- 在 Memory 面板抓 Heap Snapshot。
- 对比操作前后的快照,重点看:
- 哪类对象数量在涨
- Retainers 是谁
- 是否存在 Detached Context / 长链路缓存
如果希望在接近内存上限时自动留证,还可以使用:
node --max-old-space-size=512 --heapsnapshot-near-heap-limit=2 app.js
这个思路很适合回答“线上快 OOM 了怎么排查”。
五、线上监控怎么做才靠谱
别只打一次日志,要看趋势。
一个实用思路:
- 每隔 10 秒采集一次
rss、heapUsed、heapTotal、external - 记录请求量、QPS、队列堆积、缓存命中率
- 观察 GC 后基线是否回落
判断标准:
- 正常抖动:峰值会上去,空闲后会回落
- 可疑泄漏:重复相同流量模型,基线持续上升
典型题 & 标准答法
Q1:process.memoryUsage() 能直接看出 V8 堆上限吗?
不能。它更适合看进程内存的当前占用。要看堆上限,更稳的是 v8.getHeapStatistics().heap_size_limit。
Q2:为什么 rss 很大,但 heapUsed 并不高?
因为 rss 是整个进程驻留内存,不只包含 V8 堆,还包含原生模块、Buffer、代码段、栈等;所以 rss 大不等于 JS 堆泄漏。
Q3:想定位具体泄漏对象,用哪个 API 最合适?
不是某个简单 API,而是 Heap Snapshot。前面的数字类 API 更像“报警器”,Heap Snapshot 才像“现场勘验工具”。
常见追问
external和arrayBuffers为什么经常一起看?- 为什么
heapTotal会大于heapUsed? - 为什么看起来“对象删掉了”,
rss还是没有立刻下降?
易错点 / 坑
- 把
rss当成 V8 堆大小。 - 把
heapUsed的一次上涨直接等同于内存泄漏。 - 只看一瞬间数据,不看长期趋势。
- 不区分“看占用”和“找引用链”这两类工具。
速记要点(可背诵)
process.memoryUsage()看进程视角,v8.getHeapStatistics()看堆视角,Heap Snapshot 看对象视角。rss是总占用,heapUsed才是已使用的 V8 堆。- 先看趋势,再看分代,最后再抓快照找 Retainers。
- 关于 V8 堆上限和设计原因,可继续看:v8-memory-limit-and-design。