Q24 · 问题排查
页面运行久了越来越卡,甚至内存飙升,你怎么排查?
⚡ 速记答案(30 秒)
- 用 Memory 工具看:是否有组件卸载后仍有引用(事件监听没解绑、定时器没清掉)
- 是否有大数组 / Map 一直增长不释放
- 用 Performance 录制长时间操作:看是否有定时任务高频跑而又做了很多事
- 代码层面:清理无用订阅 / 监听;大数据结构按需丢弃或分页
📖 详细讲解
内存泄漏常见原因
1. 事件监听未清理
// ❌ 错误
useEffect(() => {
window.addEventListener('resize', handleResize);
}, []);
// ✅ 正确
useEffect(() => {
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, []);2. 定时器未清理
// ✅ 正确
useEffect(() => {
const timer = setInterval(fn, 1000);
return () => clearInterval(timer);
}, []);3. 闭包引用
• 闭包持有大对象引用
• 回调函数持有过期状态
4. 全局缓存无限增长
• Map/Set 只增不删
• 数组无限 push
排查工具
| 工具 | 用途 |
|---|---|
| Heap Snapshot | 查看内存占用 |
| Allocation Timeline | 追踪内存分配 |
| Allocation Sampling | 采样分析 |
💻 代码示例
内存泄漏排查脚本
// 在控制台执行,检测 detached DOM 节点
function findDetachedNodes() {
const walker = document.createTreeWalker(
document.body,
NodeFilter.SHOW_ELEMENT,
null
);
const detached: Element[] = [];
let node;
while (node = walker.nextNode()) {
if (!document.body.contains(node as Element)) {
detached.push(node as Element);
}
}
console.log('Detached nodes:', detached.length);
return detached;
}
// 监控内存使用
function monitorMemory() {
if (!performance.memory) {
console.log('performance.memory not available');
return;
}
setInterval(() => {
const { usedJSHeapSize, totalJSHeapSize } = performance.memory;
console.log(
'Memory:',
(usedJSHeapSize / 1024 / 1024).toFixed(2), 'MB /',
(totalJSHeapSize / 1024 / 1024).toFixed(2), 'MB'
);
}, 5000);
}面试技巧:回答性能问题时,先说指标和标准,再讲优化手段,最后结合实际项目经验。