JavaScript 内存揭秘:堆(Heap) vs 栈(Stack)

🧠 JavaScript 内存揭秘:堆(Heap) vs 栈(Stack)

在 JavaScript 引擎(如 V8)中,内存主要分为两个区域:栈内存(Stack) 和 堆内存(Heap) 。

它们就像公司的办公桌 和仓库,分工明确,协作高效。

📂 目录

  1. [🏢 核心比喻:办公桌 vs 仓库](#🏢 核心比喻:办公桌 vs 仓库)
  2. [📦 栈(Stack):快速、有序、自动清理](#📦 栈(Stack):快速、有序、自动清理)
  3. [🏭 堆(Heap):庞大、无序、手动回收](#🏭 堆(Heap):庞大、无序、手动回收)
  4. [💻 代码实战:数据是如何存储的?](#💻 代码实战:数据是如何存储的?)
  5. [🔄 垃圾回收:谁在打扫战场?](#🔄 垃圾回收:谁在打扫战场?)
  6. [💡 总结与面试考点](#💡 总结与面试考点)

1. 🏢 核心比喻:办公桌 vs 仓库

为了通俗易懂,我们把浏览器内存想象成一家公司:

📌 栈(Stack) = 员工的办公桌

  • 特点:空间小,但就在手边,存取速度极快。
  • 用途:存放正在处理的临时数据(如函数调用、局部变量)。
  • 管理:老板(JS 引擎)严格管理。你离开座位(函数执行结束),桌子上的东西立刻被清空。
  • 结构:先进后出(LIFO),像叠盘子一样。

📌 堆(Heap) = 公司的公共仓库

  • 特点:空间巨大,但距离远,存取速度相对较慢。
  • 用途:存放大件物品、复杂对象(如大数组、复杂对象、闭包)。
  • 管理:比较松散。东西扔进去就不管了,直到仓库满了,清洁工(垃圾回收器 GC)才会来清理没人用的东西。
  • 结构:无序,像一个巨大的杂物间,需要通过"地址标签"才能找到东西。

2. 📦 栈(Stack):快速、有序、自动清理

✅ 存储内容

  • 基本数据类型 :Number, String, Boolean, Null, Undefined, Symbol, BigInt。
  • 执行上下文:函数调用栈(Call Stack),记录当前执行到哪里了。

⚙️ 工作机制

  1. 分配:当声明一个变量或调用一个函数时,内存会自动在栈顶分配一块固定大小的空间。
  2. 释放 :当函数执行完毕或变量超出作用域时,这块内存会自动弹出并释放。
  3. 速度:极快,因为不需要查找,直接操作栈顶指针。

注意:在 JS 中,短字符串有时也会存储在栈中(取决于引擎优化),但逻辑上我们将其视为值类型。


3. 🏭 堆(Heap):庞大、无序、手动回收

✅ 存储内容

  • 引用数据类型 :Object, Array, Function, Date, RegExp 等。
  • 大型数据:无论数据多大,都存放在堆中。

⚙️ 工作机制

  1. 分配:当创建一个对象时,引擎会在堆中开辟一块空间存放数据。
  2. 引用 :栈中只存储一个指针(内存地址),指向堆中的这块数据。
  3. 释放 :不会自动立即释放 。需要依靠浏览器的垃圾回收机制(Garbage Collection, GC) 来定期扫描,找出不再被引用的对象并清除。

4. 💻 代码实战:数据是如何存储的?

让我们通过代码看看栈和堆是如何协作的。

场景一:基本类型(全在栈中)

javascript 复制代码
let a = 10;
let b = a; // 拷贝值
b = 20;

console.log(a); // 10
console.log(b); // 20

内存图解:

text 复制代码
Stack (栈)
+-------+
| b: 20 |  <-- 修改 b,不影响 a
+-------+
| a: 10 |  <-- a 保持原值
+-------+

结论 :基本类型是值拷贝。互不影响。

场景二:引用类型(栈存地址,堆存数据)

javascript 复制代码
let obj1 = { name: "Lingma" };
let obj2 = obj1; // 拷贝地址(指针)
obj2.name = "Aliyun";

console.log(obj1.name); // "Aliyun" 😱 obj1 也被改变了!
console.log(obj2.name); // "Aliyun"

内存图解:

text 复制代码
Stack (栈)                  Heap (堆)
+----------+              +------------------+
| obj1     |------------->| { name: "Aliyun" } |
+----------+              +------------------+
| obj2     |-------------^  (同一个对象)
+----------+

结论 :引用类型是引用拷贝 (浅拷贝)。obj1 和 obj2 指向堆中的同一个对象。修改其中一个,另一个也会变。


5. 🔄 垃圾回收:谁在打扫战场?

既然堆内存不会自动释放,那什么时候清理呢?

浏览器使用 垃圾回收机制(GC) 。主流算法是 标记-清除(Mark-and-Sweep)。

🧹 工作流程

  1. 标记 :GC 从根节点(如 window、全局变量)出发,遍历所有能访问到的对象,打上"存活"标记。
  2. 清除 :遍历堆内存,那些没有被打上标记的对象,说明已经没有任何变量引用它们了,于是被判定为"垃圾",占用内存被释放。

⚠️ 内存泄漏(Memory Leak)

如果代码中存在意外的全局变量 、未清理的定时器 或循环引用,导致某些对象永远无法被 GC 标记为"可回收",内存就会越占越多,最终导致页面卡顿甚至崩溃。

常见泄漏场景:

javascript 复制代码
// 1. 意外全局变量
function leak() {
  leakedVar = "I am global now"; // 忘记写 let/var/const
}

// 2. 未清除的定时器
const timer = setInterval(() => {
  console.log("I never stop");
}, 1000);
// 如果不清除 clearInterval(timer),回调函数及其引用的变量永远不会被回收

6. 💡 总结与面试考点

特性 栈(Stack) 堆(Heap)
存储内容 基本类型、执行上下文 引用类型(对象、数组等)
大小 小,固定大小 大,动态分配
存取速度 快 慢
管理方式 自动分配与释放(LIFO) 垃圾回收机制(GC)
数据结构 线性,有序 非线性,无序
拷贝行为 值拷贝(深拷贝效果) 引用拷贝(浅拷贝)

🎯 面试高频问答

Q1: 为什么基本类型赋值互不影响,而对象赋值会相互影响?

A: 因为基本类型存在栈中,赋值是复制值;对象存在堆中,栈里只存地址,赋值是复制地址,两者指向同一块堆内存。

Q2: 什么是深拷贝?如何实现?

A : 深拷贝是在堆中开辟一块新内存,将原对象的所有层级数据完整复制一份。实现方式:JSON.parse(JSON.stringify(obj))(有局限)、递归复制、或使用 structuredClone API。

Q3: 闭包会导致内存泄漏吗?

A: 不一定。闭包会阻止外部变量被回收,这是正常现象。但如果闭包长期存在且引用了巨大的无用数据,且无法被 GC 回收,才会导致泄漏。
🚀 博主寄语 :

理解堆和栈,不仅仅是为了应付面试。

当你遇到"修改了一个对象,另一个莫名其妙也变了"的 Bug 时,你会想起堆内存的共享特性 。

当你发现页面越来越卡时,你会想起堆内存的垃圾回收。

记住口诀 :

栈小快,存基本,自动清理不费力。

堆大慢,存对象,引用拷贝要注意。

垃圾回收靠标记,内存泄漏要警惕。

希望这篇文档能帮你彻底搞懂堆和栈的区别!如果有疑问,欢迎在评论区留言。👇

喜欢这篇文章吗?记得点赞、收藏、转发哦! ❤️

相关推荐
Wang's Blog30 分钟前
Java 项目实战: 交友项目:登录链路回顾与今日任务地图
java·开发语言·状态模式·交友
砚底藏山河1 小时前
python量化:行情数据回溯
java·开发语言·python·金融·maven
今年下半年1 小时前
AI智能体项目实战记录(02):初始化 Python 项目
开发语言·人工智能·python·fastapi
Williamtym1 小时前
PickGo Lab实践:用浏览器、Python和MCP串起机器人搬运仿真
开发语言·python·机器人
DongQiShanRen2 小时前
六根硬件映射(四):归属澄清、两处不一致与卷十一总表
开发语言·人工智能·算法·rust·哈希算法·集成学习
CS创新实验室2 小时前
Python 对象“死“了之后去了哪?——引用计数与分代垃圾回收
开发语言·jvm·python
持敬chijing2 小时前
白盒测试与代码审计-js登录逆向实战
javascript·网络·安全·web安全·网络安全·网络攻击模型
quantdash_cc2 小时前
Pandas 处理分钟级股票行情:从原始数据清洗到 K 线聚合
开发语言·python·数据分析·pandas·量化交易·股票数据·quantdash
yu_zhidun2 小时前
企业电脑监控软件怎么选:别只看功能清单,先看这 6 个底层能力
开发语言·电脑·php·域智盾系统
无忧.芙桃3 小时前
C++语言原理与实践(十六):多态机制讲解
开发语言·c++·多态