渲染进程卡死检测、崩溃恢复与现场还原怎么做?
考察点
- unresponsive / render-process-gone 事件体系
- 用户数据不丢失的恢复设计
参考答案
1. 检测机制
(1)系统事件(主进程监听)
// 渲染进程崩溃(段错误、OOM 被 kill)
win.webContents.on('render-process-gone', (e, details) => {
logger.error('渲染进程崩溃:', details.reason, details.exitCode);
// reason: clean-exit / abnormal-exit / killed / crashed / oom / ...
handleCrash(win, details);
});
// 页面无响应(JS 死循环、长任务阻塞)
win.on('unresponsive', () => {
logger.warn('窗口无响应');
showUnresponsiveDialog(win); // 询问用户"等待 or 重启"
});
win.on('responsive', () => {
logger.info('窗口恢复响应');
dismissUnresponsiveDialog();
});
(2)应用层心跳(更灵敏的补充)
系统事件触发较慢(Chromium 要等几十秒),对灵敏检测可加心跳:
// preload:每秒回应主进程 ping
ipcRenderer.on('health:ping', () => {
ipcRenderer.send('health:pong', Date.now());
});
// 主进程:超时未 pong 判定异常
setInterval(() => {
win.webContents.send('health:ping');
setTimeout(() => {
if (!pongReceived) handleHang(win);
}, 5000);
}, 10000);
2. 恢复策略
(1)崩溃恢复:直接重载
function handleCrash(win, details) {
if (details.reason === 'oom') {
// OOM:提示用户减少数据量,重载可能再次 OOM
showOOMDialog();
}
// 大多数情况:重载恢复
win.reload();
}
(2)无响应处理:给用户选择权
// 弹原生对话框:等待 / 强制重启页面
const { response } = await dialog.showMessageBox({
message: '页面无响应',
buttons: ['继续等待', '重新加载'],
});
if (response === 1) win.reload();
(3)崩溃循环防护
let crashCount = 0;
win.webContents.on('render-process-gone', () => {
crashCount++;
if (crashCount > 3) {
// 短时间内反复崩溃:进入安全模式(禁用插件/清空状态/回退首页)
enterSafeMode(win);
} else {
win.reload();
}
});
3. 现场还原(崩溃后用户数据不丢)
(1)草稿自动保存(编辑器类核心)
// 渲染进程:内容变更防抖持久化
const saveDraft = debounce((content) => {
// 优先 localStorage(同步、崩溃瞬间也可能保住)
localStorage.setItem('draft:doc-1', JSON.stringify({
content,
savedAt: Date.now(),
}));
}, 1000);
editor.on('change', saveDraft);
(2)关键状态定期快照
// 每 30s 把工作区状态快照发给主进程落盘
setInterval(() => {
window.api.saveWorkspaceSnapshot(collectWorkspaceState());
}, 30_000);
(3)崩溃后恢复提示
// 页面重载后检测草稿
onMounted(() => {
const draft = localStorage.getItem('draft:doc-1');
if (draft && isNewerThanServer(draft)) {
showRestoreBanner({
onRestore: () => restoreDraft(JSON.parse(draft)),
onDiscard: () => localStorage.removeItem('draft:doc-1'),
});
}
});
4. 日志与归因
崩溃恢复的同时要留证据(见「崩溃日志收集」):
render-process-gone的 details(reason/exitCode)随崩溃上报- 崩溃前操作路径(用户行为埋点的最后 N 条)帮助复现
- OOM 类崩溃附带内存水位数据
5. 常见卡死原因速查
| 原因 | 特征 |
|---|---|
| JS 死循环 | CPU 100%,unresponsive 触发 |
| 同步大 IPC/大对象克隆 | 操作特定功能时卡 |
| 内存膨胀触发频繁 GC | 越来越卡直到 OOM |
| GPU 进程问题 | 花屏/白屏伴随卡死 |
| 主进程阻塞拖累 | 所有窗口同时卡(不是单个窗口卡) |
6. 面试话术
检测双轨:系统事件(render-process-gone 管崩溃、unresponsive 管卡死)+ 应用层心跳(更灵敏)。恢复分级:崩溃直接 reload、无响应给用户等待/重载选择权、短时间反复崩溃进安全模式。现场还原靠防抖草稿(localStorage 优先)+ 定期快照到主进程,重载后提供恢复横幅。所有崩溃都带 details 和操作轨迹上报,形成归因闭环。
常见追问
- 重载会丢路由状态吗? SPA 配合 URL 路由可恢复到原页面;复杂工作区状态靠快照恢复。
- 主进程卡死呢? 主进程无 unresponsive 类事件可自救,靠外部 watchdog(见「主进程异常捕获与崩溃守护」),所以主进程要极度克制不做重活。