JavaScript内存泄漏排查:heap snapshot分析

Quinn862 +0/-0 0 0 正常 2025-12-24T07:01:19 JavaScript · 内存泄漏

JavaScript内存泄漏排查:heap snapshot分析

问题复现

最近项目中遇到一个严重问题:页面在长时间使用后会越来越卡顿,最终出现页面假死现象。通过Chrome DevTools的Heap Snapshot分析发现,DOM节点和闭包引用持续增长。

复现步骤:

  1. 创建一个包含大量动态列表的页面(5000+条数据)
  2. 每个列表项都绑定事件处理器并创建闭包引用
  3. 不断添加删除列表项,但未正确清理事件监听器
  4. 使用Performance Monitor观察内存使用情况

核心问题分析

通过heap snapshot发现以下问题:

  • 1500+个DOM节点被意外保留(未被垃圾回收)
  • 800+个闭包引用持续存在
  • 事件监听器未正确移除导致内存泄漏

实际代码示例

// ❌ 存在内存泄漏的代码
function createList() {
  const container = document.getElementById('list');
  for (let i = 0; i < 5000; i++) {
    const item = document.createElement('div');
    item.textContent = `Item ${i}`;
    
    // 绑定事件但未清理
    item.addEventListener('click', function() {
      // 闭包引用了外部变量
      console.log(`clicked item ${i}`);
    });
    
    container.appendChild(item);
  }
}

优化策略

1. 正确清理事件监听器

// ✅ 优化后代码
function createList() {
  const container = document.getElementById('list');
  const listeners = [];
  
  for (let i = 0; i < 5000; i++) {
    const item = document.createElement('div');
    item.textContent = `Item ${i}`;
    
    const handler = function() {
      console.log(`clicked item ${i}`);
    };
    
    item.addEventListener('click', handler);
    listeners.push({element: item, handler});
    container.appendChild(item);
  }
  
  // 清理函数
  return () => {
    listeners.forEach(({element, handler}) => {
      element.removeEventListener('click', handler);
    });
  };
}

2. 使用WeakMap避免循环引用

const eventMap = new WeakMap();

function attachListener(element) {
  const handler = function() {
    console.log('clicked');
  };
  
  element.addEventListener('click', handler);
  eventMap.set(element, handler);
}

性能对比数据

优化前:内存使用持续增长,10分钟后达到250MB 优化后:内存稳定在80MB,无明显增长趋势

通过heap snapshot分析,我们发现内存泄漏主要来源于未清理的DOM引用和闭包,建议定期进行性能检测和内存分析。

推广
广告位招租

讨论

0/2000