Q24 · 问题排查

页面运行久了越来越卡,甚至内存飙升,你怎么排查?

内存泄漏Memory调试

⚡ 速记答案(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);
}
💡
面试技巧:回答性能问题时,先说指标和标准,再讲优化手段,最后结合实际项目经验。