原稿:语雀 · JS内存管理 · 原目录:JavaScript › JS内存管理

之前就想总结,拖拖拖,拖到今天写深拷贝时才整理

JS内存模型

JS引擎将内存空间分为两块:堆(heap)与栈(stack)

  • JS中的原始数据类型值通常都存储在栈中(Number String Null Undefined Boolean Symbol)
  • 而引用类型的数据都被存储在堆中(Object)

当使用原始数据值运算时,我们就是在操纵栈中的数据;但JS不允许直接访问堆空间,我们只能在栈中保存指向堆中数据的引用/指针/地址,在操作对象时,实际上是在操作对象的引用而不是实际的对象

例子:这段代码中不同变量实际的内存空间如下图所示

var a1 = 0;   // 栈
var a2 = 'this is string'; // 栈
var a3 = null; // 栈

var b = { m: 20 }; // 变量b存在于栈中,{m: 20} 作为对象存在于堆内存中
var c = [1, 2, 3]; // 变量c存在于栈中,[1, 2, 3] 作为对象存在于堆内存中
JS内存管理 配图

函数执行中的内存管理

一个函数执行前后的内存管理的过程是什么样的?

\[强烈建议配合[JS运行机制](https://www.yuque.com/gesidalijiashidai/fgeemd/gi0181)中函数编译执行过程观看\]
  • 首先函数本身作为对象,在被声明后就创建在了堆上
  • 当函数被调用,引擎会在执行栈上创建函数的上下文
  • 函数中的基本类型数据会直接被存放在栈空间上
  • 函数中的引用类型数据会存放在堆空间上,但为了使用堆上的数据,函数上下文会在栈中保存一个指向堆上数据的引用,实际上该过程刚才已经存在——全局上下文在栈上保存了一个指向函数的引用,函数对象本身存在堆上等待调用
  • 函数执行结束,则它的上下文出栈,栈空间被回收,如果堆中对象不再被使用,则堆空间会被垃圾回收程序重新回收

C语言内存管理

为什么这里要提C语言,因为v8引擎基于C++,而引擎负责JS代码执行过程中的内存管理,换句话说JS的内存管理实际上就是C++内存管理的封装,了解C中的内存机制可以让你理解为什么会有heap与stack这样的数据结构

有兴趣和基础的同学可以去看我在系统级编程中做的关于内存布局和分配的笔记(Ch6 内存布局和分配

语雀引用内容

stack与heap的出现主要是适应了不同的数据存储需求

  • stack适合静态分配(也可动态):在处理程序源代码时分配内存空间,在程序执行之前进行,效率比较高,但因为需要提前分配,对于数据的大小需要预知,因此适合有明确类型的数据,如int、double、指针等
  • heap只能动态分配:程序在执行过程中再进行内存分配,有更大的地址空间可分配,同时更灵活,但是分配效率会降低

以上的描述针对C程序,但是JS内存管理大致基于此封装,只不过静态动态分配的过程都被JS引擎负责了,归根结底,stack与heap的划分是为了适应不同的数据存取需求