堆和栈(Heap & Stack)是程序运行时内存管理的两个核心区域,用于存储不同类型的数据。
堆和栈是 计算机基础 中 数据结构 的知识内容,更多详细内容可查看对应章节。
let a = 10; // 值 10 存储在栈中
let b = "hello"; // 值 "hello" 存储在栈中
function foo() {
let x = 5; // x 在栈中
return x + 1;
}
foo(); // 调用时创建栈帧
栈顶
├── 函数B局部变量 y=20
├── 函数B返回地址
├── 函数B参数
├── 函数A局部变量 x=10
├── 函数A返回地址
└── 函数A参数
栈底
let obj = { name: "John" }; // 对象存储在堆中
let arr = [1, 2, 3]; // 数组存储在堆中
// 创建对象(堆中分配内存)
let person = { age: 30 }; // 对象在堆中
// 垃圾回收自动释放不再使用的内存
person = null; // 原对象变为垃圾,等待回收
let a = 10; // 栈:a = 10
let b = a; // 栈:b = 10(值拷贝)
b = 20; // 栈:b = 20,a 不变
console.log(a); // 10
console.log(b); // 20
内存布局
栈内存:
地址 变量 值
0x100 a 10
0x104 b 20 (修改后)
let obj1 = { x: 10 }; // 堆中创建对象,栈中存储引用
let obj2 = obj1; // 拷贝引用,指向同一对象
obj2.x = 20; // 修改堆中的对象
console.log(obj1.x); // 20(obj1也受影响)
内存布局:
栈内存: 堆内存:
变量 地址 地址 对象
obj1 → 0x200 → 0x200 { x: 20 }
obj2 → 0x200
function process(obj) {
let local = 5; // 局部变量在栈中
obj.value = local * 2; // 修改堆中的对象
return obj;
}
let data = { value: 1 }; // 对象在堆中,引用在栈中
let result = process(data);
内存变化:
调用前:
栈:data → 堆地址
堆:{ value: 1 }
调用中:
栈:data → 堆地址
参数obj → 同一堆地址
局部变量local = 5
调用后:
堆:{ value: 10 } // 对象被修改
栈:result → 同一堆地址
| 特性 | 栈(Stack) | 堆(Heap) |
|---|---|---|
| 存储内容 | 原始类型、函数调用信息 | 对象、数组等引用类型 |
| 分配方式 | 系统自动分配/释放 | 动态分配,垃圾回收 |
| 访问速度 | 快(直接内存访问) | 慢(间接引用访问) |
| 内存大小 | 较小且固定(通常几 MB) | 较大且动态增长(可达 GB) |
| 内存碎片 | 无(连续分配) | 可能有(需垃圾回收整理) |
| 生命周期 | 随函数调用结束释放 | 不确定,由 GC 决定 |
function createCounter() {
let count = 0; // 本应在栈中,但闭包使其进入堆
return function () {
count++; // 访问堆中的count
return count;
};
}
// count变量被闭包引用,从栈"逃逸"到堆
let s1 = "hello"; // 短字符串可能在栈中
let s2 = "a".repeat(10000); // 长字符串在堆中
// 避免在栈中创建大对象
function process() {
let largeArray = new Array(1000000); // 不好:可能造成栈溢出
// 改为:
let largeArray = []; // 在堆中分配
}
// 无限递归导致栈溢出
function recursive() {
recursive(); // 不断创建栈帧
}
// 对象被意外持有,无法回收
let cache = [];
function leak() {
let data = new Array(1000000);
cache.push(data); // 持续增长,不释放
}
// 浅拷贝(共享堆对象)
let original = { a: 1, b: { c: 2 } };
let shallowCopy = { ...original };
shallowCopy.b.c = 3; // 影响original.b.c
// 深拷贝(创建新堆对象)
let deepCopy = JSON.parse(JSON.stringify(original));