目录
1.被忽视的内存管理
JavaScript不像C、C++等语言——程序员必须通过调用内存管理接口,比如 malloc()和free(),自己手动分配和释放内存——JS引擎会”自动“管理内存。也就是说,JS在创建变量(对象,字符串等)时分配内存,并且在执行完毕,将不再使用的变量的内存空间释放。这种自动化的管理方式,使得JS入门简单、开发快,但同时也让很多人忽视了对JS内存的管理与优化。
2.内存空间划分
内存空间就像是我们的房间,存储在内存中的变量和数据就是我们日常使用的物品,房间合理规划,物品分类收纳,才能方便以后使用。同样的,JS内存划分为栈(stack)和堆(heap),分别用于存储JS两种类型的数据——基本类型和引用类型。
事实上,JS并没有对内存进行严格意义上的划分,我们简单的认为JS的所有数据都是在堆中进行操作。但是JS的某些场景,仍然需要基于堆栈结构的思想去处理,比如JavaScript的执行上下文(执行上下文在逻辑上实现了堆栈,关于这一点我会在下一篇JS运行机制文章中介绍),所以说理解堆栈结构的原理与特点十分重要。
2.1栈与基本类型数据
栈的结构很像桶装薯片,入栈、出栈操作都是在栈顶进行,它的特点就是后进先出(LIFO),栈一般用于保存固定大小的值。JS的5种基本类型数据:Number、String、Boolean、Null和Undefined就在栈中分配空间。
基本类型的值指的是简单的数据段,通过访问保存值的变量直接可以操作值,所以说基本类型是按值访问的。
2.2堆与引用类型数据
堆是没有结构的,用于为一些数据大小不确定的复杂类型(引用类型)数据分配空间,例如对象、数组。
引用类型值指的是由多个值构成的对象。JS不允许直接访问堆内存中的位置,因此我们不能直接操作对象的堆内存空间。JS提供了一种间接的方式,将对象在堆中的引用(地址)保存到变量中,使用变量时,就间接地通过引用(地址)访问对象属性。因此,引用类型的值都是按引用访问的。
3.内存生命周期
内存的生命周期分为三个阶段:
- 分配内存(allocate memory):在内存中开辟空间,存储变量和数据。在JS中,当我们声明变量、函数、对象的时候,系统会自动为它们分配内存。
- 使用内存(use memory):使用之前分配过的内存。JS在代码执行时,需要对(分配过内存空间的)数据和变量进行计算,也就是对内存进行读写操作。
- 释放内存(release memory):释放不再使用的内存,变量和数据也随之销毁。代码执行结束,JS的垃圾收集机制,会自动销毁不再使用的变量和数据,释放内存。
为了我们先举个简单的例子来解释一下内存的生命周期:
var num = 1024; // 在栈中分配空间给数值变量
console.log(num + 10); // 使用变量,会对读写内存
num = null; // 使用完毕,释放内存空间
下面详细讲解一下这三个过程,着重释放内存的过程。
2.1分配内存
当我们声明变量、函数,并初始化时(未初始化,默认值undefined),JS引擎在内存中自动为它们开辟空间。
var num = 1024; // 在栈中分配空间给变量,赋值1024
var str = 'this is string'; // 在栈中分配空间给变量,赋值'this is string'
var boo = true; // 在栈中分配空间给变量,赋值true
var a = null; // 在栈中分配空间给变量,赋值null
var b; // 在栈中分配空间给变量,赋值undefined
var arr = [11, 22, 33]; // 在栈中分配空间给变量,在堆中分配空间存储[11, 22, 33],并将数据在堆中的地址赋值给变量
var person = {
name: 'Json', age: 26 }; // 在栈中分配空间给变量,在堆中分配空间存储{ name: 'Json', age: 26 },并将数据在堆中的地址赋值给变量