持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第5天,点击查看活动详情
栈内存与堆内存
对于 JS 不同类型的数据来说,基本数据类型(string/number/boolean/undefined/null)的值存储在栈内存中,引用数据类型(object)的值存储在堆内存中,程序代码存储在文本段中。(以上只是简单理解,并不尽然,不同引擎的实现和优化并不相同)
注意,前面的用词 「的值」 ,比如 a = { id: 1 } ,存储在堆内存中的是对象 { id: 1 } ,而 变量标识符 a 则存储在栈内存中。
即,栈中存储的是 a=0x0f00000,堆中的地址 0x0f00000 存储的是 { id: 1 }。
注意:对象中如果有的 key 对应的 value 是基本类型,如 string ,它也是存在堆中的。
什么是垃圾
要说垃圾回收,首先要理解哪些内存是垃圾,有用的内存什么时候变成的垃圾。
function fn() {
let a = { id: 1 };
}
fn();
我们知道,函数调用存储于栈内存中,由系统(运行时环境)自动分配和回收内存。因此,对于函数 fn,它执行于栈内存中。
函数中的局部变量也存储在栈内存中,于是,变量标识符 a 和它的内容也存储在栈内存中。
然而,a 的内容(值)是一个引用数据类型,它会被存储到堆内存中,而把内存虚拟地址当做值赋给 a。
当函数执行结束后,根据栈内存管理规则,该函数的执行环境会被从栈内存顶部弹出,于是 a=0x0f00000 被从栈内存清除,接着 fn 从栈内存清除。
至此程序执行完毕(我们假设该进程还未退出),此时栈内存是干净的(可以理解为里面的东西用完就扔),但是堆内存中 0x0f00000 位置上还存着一个对象,这个对象在之后的程序中永远都不会被访问到,因为这个对象在堆中分配内存时,只把地址告诉了 fn 中的 a,而 a 是函数的局部变量,它并未在 fn 的执行周期中把这个堆地址传给外面,于是 fn 执行完毕后就被永久性清除了,也就是说世界上再也没有人知道 { id: 1 } 这个数据住在哪里,那么这个对象对于当前执行的程序来说,就是没有意义的,但是却占用了程序内存空间,于是被看做了垃圾。