栈内存与堆内存
对于 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 }
这个数据住在哪里,那么这个对象对于当前执行的程序来说,就是没有意义的,但是却占用了程序内存空间,于是被看做了垃圾。