Electron ProcessMemoryInfo 对象全解:process.getProcessMemoryInfo() 的字段、平台差异与 Chromium 内存采集实现
本文基于 Electron 官方结构文档 process-memory-info.md 及其父级 API 文档 process.md,结合 Electron 仓库中 shell/common 与 shell/browser 目录下的真实源码,完整解析 ProcessMemoryInfo 对象的三个字段(residentSet / private / shared)、其唯一的获取入口 process.getProcessMemoryInfo()、跨平台取值差异、错误场景,以及数据如何从 Chromium 的内存采集框架(Memory Instrumentation)一路传递到 JavaScript 层。读完本文,你可以直接在自己的 Electron 应用中落地内存监控,并能准确解释各字段在不同操作系统上的语义差异。
ProcessMemoryInfo 是什么
ProcessMemoryInfo 是 Electron process 对象上 process.getProcessMemoryInfo() 方法的返回结构,用于给出当前进程的内存占用统计。根据 process.md 的定义:
- 返回值类型为
Promise<ProcessMemoryInfo>,是一个异步 API; - 所有统计量均以 Kilobytes(KB) 为单位;
- 必须在
appready 之后调用,否则 Promise 会被 reject。
它是排查 Electron 应用内存增长、内存泄漏问题的第一手数据来源之一,常与 app.getAppMetrics() 返回的每个子进程 ProcessMetric 中的 MemoryInfo 字段配合使用——前者看“当前这个进程(通常是主进程)”,后者看“整棵进程树里的每个进程”。
字段详解
原始结构文档 process-memory-info.md 定义了如下三个字段,下文逐一展开:
| 字段 | 类型 | 可用平台 | 语义(按原文档) |
|---|---|---|---|
residentSet |
Integer | Linux、Windows | 当前被钉在实际物理 RAM 中的内存量(KB) |
private |
Integer | 全平台 | 不被其他进程共享的内存量,例如 JS 堆、HTML 内容(KB) |
shared |
Integer | 全平台 | 进程间共享的内存量,通常是 Electron 代码本身占用的部分(KB) |
residentSet:即“常驻集”大小,反映进程当前真正占用物理内存的规模,是评估内存压力最直观的指标。注意它仅在 Linux 与 Windows 上有值——这一点与源码中的条件编译严格对应(见下文),在 macOS 上该字段不会出现。private:私有内存,对应进程自己申请的、不与别的进程共享的部分,典型内容就是 V8/JS 堆与页面 DOM 数据。文档明确指出 macOS 上“私有内存更能代表压缩前的真实内存占用”,因此跨平台比较进程内存时,优先以private为准。shared:共享内存,通常是各进程共同映射的 Electron/Chromium 二进制代码段(.so/.dll 等)。它可以为 0,这是正常现象,官方测试里也显式只断言它大于 -1(即存在即可)。
API 入口:process.getProcessMemoryInfo()
基本用法
const { app } = require('electron')
app.whenReady().then(async () => {
try {
const memory = await process.getProcessMemoryInfo()
console.log('residentSet:', memory.residentSet, 'KB') // Linux / Windows
console.log('private: ', memory.private, 'KB')
console.log('shared: ', memory.shared, 'KB')
} catch (err) {
// 常见原因见下文“错误场景”一节
console.error('Failed to get memory info:', err.message)
}
})
要点:
- 返回值是 Promise,需要
await或.then(); - 官方文档明确要求 “This api should be called after app ready”,即在
app.whenReady()之后再调用; - 所有数值单位为 KB,换算 MB 时除以 1024。
在沙箱化渲染进程中的可用性
根据 process.md 的 “Sandbox” 一节,沙箱化渲染进程中的 process 对象只暴露一个 API 子集,其中包含 getProcessMemoryInfo()。而从源码结构看,electron_bindings.cc 中只有满足 electron::IsBrowserProcess() 时才注册该绑定,且实现内部带有 CHECK(electron::IsBrowserProcess()) 断言——也就是说,完整的 Promise 实现与数据供给链路是建立在浏览器进程之上的。
源码实现:数据从哪里来
Electron 并没有自己读取 /proc 或调用平台 API,而是复用了 Chromium 的 Memory Instrumentation(全局内存转储)框架。核心代码位于 shell/common/api/electron_bindings.cc。
第一步:发起全局内存转储请求
GetProcessMemoryInfo(electron_bindings.cc#L203-L221)做了三件事:
v8::Local<v8::Promise> ElectronBindings::GetProcessMemoryInfo(v8::Isolate* isolate) {
CHECK(electron::IsBrowserProcess());
gin_helper::Promise<gin_helper::Dictionary> promise(isolate);
v8::Local<v8::Promise> handle = promise.GetHandle();
if (!Browser::Get()->is_ready()) {
promise.RejectWithErrorMessage(
"Memory Info is available only after app ready");
return handle;
}
memory_instrumentation::MemoryInstrumentation::GetInstance()
->RequestGlobalDumpForPid(
base::GetCurrentProcId(), {} /* allocator_dump_names */,
base::BindOnce(&ElectronBindings::DidReceiveMemoryDump,
std::move(promise), base::GetCurrentProcId()));
return handle;
}
- 先检查
Browser::Get()->is_ready(),未 ready 时立即 reject,报错信息为Memory Info is available only after app ready——这就是文档要求“app ready 后再调用”的底层原因; - 然后调用
MemoryInstrumentation::GetInstance()->RequestGlobalDumpForPid(...),请求一份覆盖当前进程(base::GetCurrentProcId())的全局内存转储; - 回调
DidReceiveMemoryDump携带 Promise,异步完成后解析。
第二步:从全局转储中提取三个字段
DidReceiveMemoryDump(electron_bindings.cc#L236-L272)遍历转储结果中所有进程的 ProcessDump,找到 pid 匹配的那一项,再从操作系统的 os_dump() 中取数:
const auto& osdump = dump.os_dump();
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_WIN)
dict.Set("residentSet", osdump.resident_set_kb);
#endif
dict.Set("private", osdump.private_footprint_kb);
dict.Set("shared", osdump.shared_footprint_kb);
这里可以直接解释文档中的两处平台差异:
residentSet仅 Linux / Windows 有:源码用#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_WIN)条件编译,macOS 分支根本不写这个键。官方文档给出的原因是:macOS 会对近期未使用的内存页做内存压缩(in-memory compression),导致常驻集大小不能真实反映占用,因而 Chromium 在 macOS 上不提供该值;private/shared全平台都有,分别来自private_footprint_kb与shared_footprint_kb。
如果转储成功但未找到当前进程条目,Promise 会被 reject,错误信息为 Failed to find current process memory details in memory dump;转储请求本身失败则为 Failed to create memory dump。
顺带一提:浏览器侧的另一套 ProcessMemoryInfo
shell/browser 下还有一个同名 C++ 结构体(注意这是内部实现类型,不是 JS 层对象):process_metric.h#L17-L25 定义了 working_set_size、peak_working_set_size、private_bytes 三个成员,并在 process_metric.cc 中按平台实现:Windows 通过 GetProcessMemoryInfo / PROCESS_MEMORY_COUNTERS_EX 读取,macOS 通过 task_info(MACH_TASK_BASIC_INFO) 读取。它服务于按子进程维度的指标采集路径,与 process.getProcessMemoryInfo() 返回的 JS 结构同名但不同源,阅读源码时注意区分。
错误场景与边界情况汇总
结合文档与源码,调用方需要处理的异常路径有:
| 场景 | 结果 | 依据 |
|---|---|---|
app 未 ready 就调用 |
Promise reject:Memory Info is available only after app ready |
electron_bindings.cc#L209-L213 |
| 内存转储请求失败 | reject:Failed to create memory dump |
electron_bindings.cc#L247-L250 |
| 转储成功但缺少当前进程条目 | reject:Failed to find current process memory details in memory dump |
electron_bindings.cc#L268-L271 |
| macOS 环境 | 返回对象中没有 residentSet 字段 |
条件编译,见上文 |
shared 为 0 |
正常现象,不是错误 | 官方测试断言 shared > -1 |
官方测试 spec/api-process-spec.ts#L42-L53 对这些边界做了明确验证:
describe('process.getProcessMemoryInfo()', () => {
it('resolves promise successfully with valid data', async () => {
const memoryInfo = await invoke(() => process.getProcessMemoryInfo());
expect(memoryInfo).to.be.an('object');
if (process.platform === 'linux' || process.platform === 'win32') {
expect(memoryInfo.residentSet).to.be.a('number').greaterThan(0);
}
expect(memoryInfo.private).to.be.a('number').greaterThan(0);
// Shared bytes can be zero
expect(memoryInfo.shared).to.be.a('number').greaterThan(-1);
});
});
这段测试与结构文档、C++ 条件编译三者完全互相印证:residentSet 仅在 Linux/Windows 断言大于 0,private 恒大于 0,shared 允许为 0。
与相邻内存 API 的对比
process 对象上还有一组容易混淆的内存 API,定位各不相同:
| API | 返回 | 粒度 | 说明 |
|---|---|---|---|
process.getProcessMemoryInfo() |
Promise<ProcessMemoryInfo> |
当前进程 | 本文主角,走 Memory Instrumentation |
process.getBlinkMemoryInfo() |
{ allocated, total }(KB) |
当前进程的 Blink 堆 | 调试渲染/DOM 内存问题,同步返回 |
process.getHeapStatistics() |
V8 堆统计(KB) | 当前进程的 V8 堆 | 同步返回 |
process.getSystemMemoryInfo() |
系统级内存(KB) | 整台机器 | 含 free、Linux 的 available、macOS 的 fileBacked/purgeable、Win/Linux 的 swap 信息 |
系统级数据同样可以在 electron_bindings.cc#L165-L197 中看到平台分支:free 在 Windows 上取 avail_phys,Linux 额外提供 available(对应 /proc/meminfo 的 MemAvailable),macOS 额外提供 fileBacked/purgeable。做内存监控时,常见的组合是:用 getSystemMemoryInfo() 判断系统整体压力,用 getProcessMemoryInfo() 看当前进程,再用 getBlinkMemoryInfo() / getHeapStatistics() 区分“是 Blink/DOM 涨的还是 V8 堆涨的”。
实战建议
- 跨平台代码只依赖
private与shared;需要常驻集时先判断typeof memory.residentSet === 'number',再决定展示逻辑; - 始终包在
app.whenReady()之后并处理 reject,因为该 API 有明确的 ready 前置条件与三条错误路径; - 采样策略:该 API 每次调用都会触发一次全局内存转储请求,属于异步且相对重的操作,适合低频采样(如配合定时器每 5~10 秒一次),而不是在渲染热路径中高频调用;
- 排查“内存到底涨在哪”时,把
ProcessMemoryInfo与每个子进程的 ProcessMetric(含 MemoryInfo 的workingSetSize/peakWorkingSetSize/privateBytes)以及 CPUUsage 一起采集,就能得到进程树级别的完整画像。
小结
ProcessMemoryInfo 虽然只有 residentSet、private、shared 三个字段,但它背后是 Chromium Memory Instrumentation 框架的一次完整调用链:RequestGlobalDumpForPid 发起转储 → DidReceiveMemoryDump 按 PID 定位 → 从 os_dump() 取出 resident_set_kb / private_footprint_kb / shared_footprint_kb(shell/common/api/electron_bindings.cc)。理解这条链路,你就能准确解释为什么 macOS 没有 residentSet、为什么 shared 可能为 0、为什么必须在 app ready 后调用,并据此写出健壮的跨平台内存监控代码。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0626
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00