全部学科
NodeJS全栈
nodejs
Python全栈
python
小程序首页
📅 2026-05-16 10 分钟 ✍️ juanwangdev

JavaScript 内存管理与优化

JavaScript 使用自动垃圾回收机制,但开发者仍需理解内存管理原理以避免常见问题。

内存生命周期

三个阶段

  1. 分配:变量、对象、函数创建时分配内存
  2. 使用:读写内存中的值
  3. 释放:不再使用的内存被回收

内存分配示例

JavaScript
// 基本类型 - 栈内存
let num = 42;
let str = 'hello';

// 引用类型 - 堆内存
let obj = { name: 'JS' };
let arr = [1, 2, 3];

常见内存泄漏

1. 全局变量

JavaScript
// 意外创建全局变量
function leak() {
  bar = 'leaked'; // 未声明,挂载到 window
}

// 正确做法
function noLeak() {
  'use strict';
  let bar = 'safe';
}

2. 闭包引用

JavaScript
// 闭包持有大对象引用
function createClosure() {
  const largeData = new Array(1000000);

  return function() {
    console.log(largeData.length); // 持续引用 largeData
  };
}

// 优化:只保留需要的数据
function optimizedClosure() {
  const largeData = new Array(1000000);
  const length = largeData.length; // 只保存需要的值

  return function() {
    console.log(length);
  };
}

3. 定时器未清理

JavaScript
// 泄漏:定时器持有外部引用
class Component {
  constructor() {
    this.data = new Array(1000000);
    this.timer = setInterval(() => {
      console.log(this.data.length);
    }, 1000);
  }

  // 必须清理
  destroy() {
    clearInterval(this.timer);
    this.data = null;
  }
}

4. DOM 引用

JavaScript
// 泄漏:DOM 元素被多处引用
const elements = [];
function addElement() {
  const el = document.createElement('div');
  elements.push(el); // 数组持有引用
  document.body.appendChild(el);
}

function removeElement() {
  const el = elements.pop();
  document.body.removeChild(el);
  // el 仍在 elements 数组中被引用!
}

// 正确做法
elements.pop(); // 移除引用

5. 事件监听器

JavaScript
// 泄漏:未移除事件监听
class View {
  constructor() {
    this.handleClick = this.handleClick.bind(this);
    document.addEventListener('click', this.handleClick);
  }

  handleClick() { /* ... */ }

  // 必须移除
  destroy() {
    document.removeEventListener('click', this.handleClick);
  }
}

内存优化技巧

1. 对象池模式

JavaScript
class ObjectPool {
  constructor(createFn, resetFn) {
    this.pool = [];
    this.createFn = createFn;
    this.resetFn = resetFn;
  }

  acquire() {
    return this.pool.length > 0
      ? this.pool.pop()
      : this.createFn();
  }

  release(obj) {
    this.resetFn(obj);
    this.pool.push(obj);
  }
}

// 使用示例
const vectorPool = new ObjectPool(
  () => ({ x: 0, y: 0 }),
  (v) => { v.x = 0; v.y = 0; }
);

2. 及时释放引用

JavaScript
// 处理大数组后释放
function processLargeData() {
  let data = new Array(1000000).fill(0);

  // 处理数据
  const result = data.map(x => x * 2);

  data = null; // 释放内存
  return result;
}

3. 使用 WeakMap/WeakSet

JavaScript
// WeakMap 不阻止垃圾回收
const metadata = new WeakMap();

function attachMeta(obj, meta) {
  metadata.set(obj, meta);
}

// 当 obj 被回收,meta 自动释放

内存分析工具

Chrome DevTools

  1. Memory 面板:堆快照、分配时间线
  2. Performance 面板:内存使用趋势

代码检测

JavaScript
// 检测内存使用
if (process.memoryUsage) {
  const used = process.memoryUsage();
  console.log({
    heapTotal: `${(used.heapTotal / 1024 / 1024).toFixed(2)} MB`,
    heapUsed: `${(used.heapUsed / 1024 / 1024).toFixed(2)} MB`
  });
}

使用 Chrome Memory 面板的 Heap Snapshot 功能,可以对比两次快照发现未释放的对象。

要点总结

泄漏类型原因解决方案
全局变量未声明变量使用严格模式
闭包持有大对象引用只保留必要数据
定时器未清理destroy 中 clearInterval
DOM 引用多处引用移除时清除引用
事件监听未移除removeEventListener
  • 理解栈内存与堆内存的区别
  • 及时清理定时器、事件监听器
  • 使用 WeakMap 存储元数据
  • 定期使用 DevTools 分析内存

📝 发现内容有误?点击此处直接编辑

← 上一篇 JavaScript 事件循环与宏任务/微任务
下一篇 → JavaScript 垃圾回收机制
想查看更多题目和详细解析?
小程序提供完整的题库、模拟考试和详细解析
马上就来

长按或扫描二维码,立即体验

扫码体验小程序
马上就来
使用微信扫描二维码
立即体验完整题库