首页
/ Electron ProcessMemoryInfo 对象全解:process.getProcessMemoryInfo() 的字段、平台差异与 Chromium 内存采集实现

Electron ProcessMemoryInfo 对象全解:process.getProcessMemoryInfo() 的字段、平台差异与 Chromium 内存采集实现

2026-09-06 14:43:14作者:凌朦慧Richard

本文基于 Electron 官方结构文档 process-memory-info.md 及其父级 API 文档 process.md,结合 Electron 仓库中 shell/commonshell/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) 为单位;
  • 必须在 app ready 之后调用,否则 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)
  }
})

要点:

  1. 返回值是 Promise,需要 await.then()
  2. 官方文档明确要求 “This api should be called after app ready”,即在 app.whenReady() 之后再调用;
  3. 所有数值单位为 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

第一步:发起全局内存转储请求

GetProcessMemoryInfoelectron_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,异步完成后解析。

第二步:从全局转储中提取三个字段

DidReceiveMemoryDumpelectron_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);

这里可以直接解释文档中的两处平台差异:

  1. residentSet 仅 Linux / Windows 有:源码用 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_WIN) 条件编译,macOS 分支根本不写这个键。官方文档给出的原因是:macOS 会对近期未使用的内存页做内存压缩(in-memory compression),导致常驻集大小不能真实反映占用,因而 Chromium 在 macOS 上不提供该值;
  2. private / shared 全平台都有,分别来自 private_footprint_kbshared_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_sizepeak_working_set_sizeprivate_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/meminfoMemAvailable),macOS 额外提供 fileBacked/purgeable。做内存监控时,常见的组合是:用 getSystemMemoryInfo() 判断系统整体压力,用 getProcessMemoryInfo() 看当前进程,再用 getBlinkMemoryInfo() / getHeapStatistics() 区分“是 Blink/DOM 涨的还是 V8 堆涨的”。

实战建议

  1. 跨平台代码只依赖 privateshared;需要常驻集时先判断 typeof memory.residentSet === 'number',再决定展示逻辑;
  2. 始终包在 app.whenReady() 之后并处理 reject,因为该 API 有明确的 ready 前置条件与三条错误路径;
  3. 采样策略:该 API 每次调用都会触发一次全局内存转储请求,属于异步且相对重的操作,适合低频采样(如配合定时器每 5~10 秒一次),而不是在渲染热路径中高频调用;
  4. 排查“内存到底涨在哪”时,把 ProcessMemoryInfo 与每个子进程的 ProcessMetric(含 MemoryInfoworkingSetSize / peakWorkingSetSize / privateBytes)以及 CPUUsage 一起采集,就能得到进程树级别的完整画像。

小结

ProcessMemoryInfo 虽然只有 residentSetprivateshared 三个字段,但它背后是 Chromium Memory Instrumentation 框架的一次完整调用链:RequestGlobalDumpForPid 发起转储 → DidReceiveMemoryDump 按 PID 定位 → 从 os_dump() 取出 resident_set_kb / private_footprint_kb / shared_footprint_kbshell/common/api/electron_bindings.cc)。理解这条链路,你就能准确解释为什么 macOS 没有 residentSet、为什么 shared 可能为 0、为什么必须在 app ready 后调用,并据此写出健壮的跨平台内存监控代码。

登录后查看全文
热门项目推荐
相关项目推荐