JavaScript 中的内存管理和垃圾回收机制是开发者需要了解的重要概念,特别是在构建复杂应用时,不当的内存管理可能导致内存泄漏,影响应用性能。下面我将详细讲解这些概念。

垃圾回收机制(Garbage Collection)

JavaScript 采用自动内存管理,也就是垃圾回收机制。其核心原理是:当一个对象不再被引用,或者无法从根对象(如全局变量、当前调用栈中的变量)访问到时,垃圾回收器就会将其标记为可回收对象,并在适当的时候释放其所占用的内存。

常见的垃圾回收算法

  1. 标记清除算法(Mark and Sweep) 这是最基础的垃圾回收算法,分为两个阶段:
  2. 标记整理算法(Mark and Compact) 这是标记清除算法的改进版本,在清除阶段后增加了整理内存的步骤,将存活的对象移动到连续的内存空间,减少内存碎片。
  3. 分代回收算法(Generational GC) 现代浏览器通常采用这种算法,它基于"对象存活时间越久,越不可能被回收"的假设,将对象分为:

内存泄漏(Memory Leaks)

内存泄漏是指程序中已不再使用的内存无法被垃圾回收器释放,导致内存占用持续增长。在 JavaScript 中,常见的内存泄漏场景包括:

1. 全局变量

如果意外地创建了全局变量,这些变量会一直存在于全局作用域中,不会被回收。

function leak() {
    // 意外创建全局变量
    leakedVar = "This is a memory leak";
}

2. 未清理的定时器和回调函数

当使用 setIntervalsetTimeout 时,如果忘记在不需要时清除它们,回调函数及其依赖的变量会一直存在。

// 创建定时器
const interval = setInterval(() => {
    console.log("Running...");
}, 1000);

// 没有调用 clearInterval(interval),即使不再需要

3. DOM 引用

当保存了 DOM 元素的引用,即使 DOM 元素已被从页面中移除,如果引用仍然存在,该元素及其子元素都不会被回收。

const element = document.getElementById('myElement');
// 之后移除了该元素
document.body.removeChild(element);
// 但 element 变量仍然引用该元素,导致内存泄漏

4. 闭包

闭包会捕获并保留其创建时的作用域,如果闭包被长期保留,其捕获的变量也不会被释放。