JavaScript的内存管理由引擎自动完成,开发者不需要手动分配和释放内存,但了解其底层机制能够帮助我们在开发过程中写出更健壮的代码,减少内存泄漏等问题的出现。

JavaScript内存生命周期
JavaScript的内存管理遵循三个核心阶段,分别是内存分配、内存使用和内存释放,整个过程由JavaScript引擎自动调度。
1. 内存分配
当我们在代码中声明变量、创建函数或者对象时,JavaScript引擎会自动为这些数据结构分配对应的内存空间。比如声明一个字符串变量,引擎会在栈或者堆中分配存储该字符串的内存。
// 基本类型分配在栈内存
let num = 10;
let str = "hello";
// 引用类型分配在堆内存,栈中存储的是堆内存的地址引用
let obj = { name: "test" };
let arr = [1, 2, 3];
2. 内存使用
内存分配完成后,我们就可以对分配的内存进行读写操作,比如修改变量的值、调用对象的方法、访问数组的元素等,这个阶段就是内存的使用过程。
3. 内存释放
当分配的内存不再被使用时,JavaScript的垃圾回收机制会自动回收这部分内存,避免内存占用持续升高导致应用卡顿甚至崩溃。
常见的垃圾回收算法
JavaScript引擎主要采用两种垃圾回收算法,分别是引用计数算法和标记清除算法,不同浏览器引擎可能会结合两种算法的优势做优化。
引用计数算法
引用计数的核心思路是跟踪每个值被引用的次数,当引用次数变为0时,就判定该值不再被使用,可以回收对应的内存。
具体规则如下:
- 当声明一个变量并将一个引用类型值赋给该变量时,这个值的引用次数加1
- 如果这个变量被重新赋值,或者变量离开作用域,之前引用的值的引用次数减1
- 当一个值的引用次数变为0时,就可以回收其占用的内存
但是引用计数算法存在一个致命缺陷,就是无法处理循环引用的问题,比如两个对象互相引用,即使它们已经不再被外部使用,引用次数也不会变为0,就会导致内存泄漏。
// 循环引用示例
function createCycle() {
let objA = {};
let objB = {};
// objA引用objB,objB引用objA,两者引用次数都为1
objA.b = objB;
objB.a = objA;
// 函数执行结束后,objA和objB离开作用域,但互相引用,引用次数不为0,无法被回收
}
createCycle();
标记清除算法
标记清除算法是目前主流JavaScript引擎普遍采用的垃圾回收算法,它解决了引用计数无法处理循环引用的问题。
算法的执行过程分为两个阶段:
- 标记阶段:从根对象(比如全局对象window、当前执行栈中的变量等)出发,遍历所有能访问到的对象,给这些对象标记为可达
- 清除阶段:遍历堆中所有对象,将没有被标记为可达的对象回收,释放对应的内存,同时清除之前的可达标记,为下一次回收做准备
因为标记清除是从根对象出发遍历,所以即使存在循环引用的对象,只要它们无法从根对象访问到,就会被判定为不可达,从而被回收,避免了循环引用导致的内存泄漏。
// 标记清除处理循环引用的示例
function test() {
let objA = {};
let objB = {};
objA.b = objB;
objB.a = objA;
}
// 函数执行结束后,objA和objB无法从根对象访问到,会被标记为不可达,后续回收时会被释放
test();
V8引擎的垃圾回收优化
Chrome的V8引擎在标记清除的基础上做了进一步的优化,将堆内存分为新生代和老生代两个区域,采用不同的回收策略提升效率。
| 区域 | 存储内容 | 回收算法 | 特点 |
|---|---|---|---|
| 新生代 | 存活时间较短的对象,比如临时变量、函数内的局部对象 | Scavenge算法 | 回收速度快,内存空间较小 |
| 老生代 | 存活时间较长的对象,比如全局对象、多次回收后仍然存活的对象 | 标记清除、标记整理算法 | 回收速度较慢,内存空间较大 |
开发中的内存优化建议
虽然JavaScript有自动垃圾回收机制,但我们在开发中还是可以做一些操作来减少内存占用,避免内存泄漏:
- 及时解除不再使用的引用,比如将全局变量赋值为null,避免无意义的引用占用内存
- 避免不必要的全局变量,尽量使用局部变量,局部变量在函数执行结束后会自动解除引用
- 谨慎使用闭包,闭包会持有外部函数的变量引用,容易导致这些变量无法被回收
- 移除不再需要的事件监听器,避免监听器持有DOM元素或者对象的引用
- 避免创建过大的对象或者数组,必要时可以分批次处理数据
// 优化示例:及时解除引用
let bigData = { /* 很大的数据对象 */ };
// 使用完bigData后,手动解除引用
bigData = null;
// 避免不必要的全局变量
function handleData() {
// 使用局部变量,函数执行结束后自动释放
let tempData = [1, 2, 3];
return tempData.map(item => item * 2);
}
需要注意的是,不同的JavaScript引擎实现细节可能存在差异,但核心的内存管理和垃圾回收思路是相通的,了解这些原理能够帮助我们更好地理解代码的运行逻辑,写出性能更优的代码。
JavaScript内存管理垃圾回收标记清除引用计数修改时间:2026-07-19 11:36:33