You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Electron应用打包为EXE后渲染进程内存溢出,开发模式运行正常

Electron打包后渲染进程OOM崩溃(开发模式正常)

预期行为

在主渲染窗口中从导入的模块初始化UI,开发模式下运行应用一切正常,打包为EXE后运行表现与开发模式一致。

实际表现

查看Electron日志和Windows任务管理器的内存消耗情况,内存占用持续攀升直至渲染进程崩溃,Electron打印OOM错误。
相关日志如下:
Renderer process oom
一段时间后输出日志:
[19156:0921/120104.184:ERROR:gpu_init.cc(440)] Passthrough is not supported, GL is swiftshader

实现逻辑

导入部署在localhost的自定义模块,该模块暴露Tesseract对象,通过该对象初始化UI。

主浏览器窗口组件UI初始化逻辑(index.tsx)

useEffect(() => {
if (tsToken) {
Tesseract.ui
.init(tsToken.tsToken, "random_id")
.then((sdk: any) => {
console.log("🚀 ~ file: index.js ~ line 3 ~ sdk", sdk);
// 该行之后无日志输出,渲染进程直接崩溃(开发模式下运行正常)
sdk.init({
...MasterConfig,
RootElement: document.getElementById("tesseract-root"),
});

    const authToken = sessionStorage.getItem("ts_token");
    ipcRenderer.send("set-globals", { TAC: authToken });
  })
  .catch((err: any) => console.error(err));
}
}, [tsToken]);

return <></>;

上述代码向init方法传入根元素,init方法会在该根元素中初始化一个React应用。

自定义模块init方法实现

SDK.prototype.init = async function (config) {
try {
  const parsedConfig = JSON.parse(this.META.uiConfig);
  window.Tesseract.ui["MasterConfig"] = merge(parsedConfig, config);
  window.Tesseract.ui["info"] = this.META;

  const projectType = window.Tesseract.ui.MasterConfig.ProjectConfig.type;
  console.log("🚀 ~ file: lib.js ~ line 51 ~ projectType", projectType)

  const MasterConfig = window.Tesseract.ui["MasterConfig"];
  console.log(MasterConfig);
  if (
    MasterConfig &&
    MasterConfig.UserProperties &&
    MasterConfig.UserProperties.userId
  ) {
    console.log("React init here");
    // 日志仅输出到此处,后续无任何报错
    try {
      RecorderUI.ReactApp({ ...parsedConfig, ...config }).then((obj) => {
        // 该行日志从未输出
        console.log("UI rendered");
        return obj;
      }).catch(err => console.log(err));
    } catch (err) {
      console.log(err);
      return err;
    }
  } else {
    const e = new Error("User Id Missing");
    e.name = "Missing unique user Id";
    throw e;
  }
} catch (err) {
  console.error(err);
  return err;
}
};

UI渲染方法实现

export default async function entry(config) {
  // 该行日志从未输出,说明崩溃发生在该方法执行前
  console.log("somemmthing something");
  console.log("Root Element: ", 
  window.Tesseract?.ui?.MasterConfig?.RootElement);
  try {
    const { RootElement } = config;
    RootElement.attachShadow({ mode: "open" });

    await IDBStore.create({
      version: 1,
      name: "ext",
      schema: {
        globals: "&key",
      },
    });

    const shadowRoot = RootElement.shadowRoot;
    const styleElement = document.createElement("style");
    styleElement.innerHTML = `${LoaderCss} ${SourcePreviewCSS} ${NotificationCss} ${LibraryCss} ${SelectModeCss} ${WebcamBoxCss} ${CameraBubbleCss} ${countdownCss} ${indexStyles} ${commonCss} ${colorsCss} ${homeCss} ${recordCss} ${advancedOptionsCss} ${recordingTypesCss} ${toggleOptionsCss} ${tourOverlayCss} ${headerCss} ${checkboxCss} ${tooltipCss} ${dropdownCss} ${toggleCss} ${recordButtonCss} ${bannersCss} ${toolsCss} ${canvasCss} ${paintCss} ${recordingControlsCss} ${controlsMainCss}`;
  const reactRoot = document.createElement("div");
  reactRoot.setAttribute("id", "react-root");
  shadowRoot.appendChild(styleElement);
  shadowRoot.appendChild(reactRoot);

  ReactDOM.render(
    <Provider store={store}>
      <App />
    </Provider>,
    reactRoot
  );
  window.addEventListener("message", handleMessaging, false);
} catch (err) {
  console.error(err);
  return new Error(err);
}
}

开发/生产模式差异及问题定位方向

  • 资源寻址差异:开发模式下加载的是localhost本地服务资源,打包后如果模块内硬编码了localhost相关资源路径,生产环境无法正常访问资源会触发逻辑层无限重试,导致内存持续上涨最终OOM。
  • 安全策略差异:生产模式默认开启更严格的沙箱限制、CSP策略,代码中用到的Shadow DOM、IndexedDB初始化操作如果被安全策略拦截,可能触发内部异常重试逻辑。
  • 硬件加速差异:日志中的GPU初始化报错说明生产环境下硬件加速 fallback 到了软渲染模式swiftshader,大量UI渲染操作走CPU软渲染会快速消耗内存,尤其是代码中一次性向Shadow DOM插入了大量CSS样式,软渲染处理大段样式计算时极易触发内存泄漏。
  • 打包优化副作用:打包工具的tree-shaking、代码压缩操作如果误剪了模块的运行时依赖,会导致初始化逻辑进入死循环,引发内存持续上涨。

解决方案

  • 优先关闭硬件加速测试:主进程创建窗口前添加代码app.disableHardwareAcceleration(),验证OOM问题是否消失。
  • 修正资源引用路径:确认自定义模块所有资源地址没有硬编码localhost,统一改为相对路径或者App内部协议路径。
  • 调整生产环境安全策略:打包配置中添加允许本地资源、IndexedDB操作的CSP规则,临时关闭不必要的沙箱限制验证问题根因。
  • 拆分CSS加载逻辑:不要一次性将所有CSS字符串拼接后插入style标签,改为按需加载或者通过link标签引入CSS文件,避免大字符串解析占用大量内存。
  • 调试生产环境渲染进程:打包时保留调试符号,启动EXE时添加启动参数--remote-debugging-port=9222,用Chrome浏览器访问调试端口连接渲染进程,查看调用栈和内存快照,定位死循环或者内存泄漏点。

内容的提问来源于stack exchange,提问作者ParfectShot

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.03 09:45:05