一. JS的数据类型
JavaScript是一种弱类型的、动态的语言
弱类型:支持隐式类型转换的语言,与强类型对应
动态语言:在运行过程中需要检查数据类型的语言,与静态语言对应
原始类型
- Boolean
- Null
- Undefined
- Number
- String
- Symbol
- BigInt
引用类型
- Object
二. 内存空间
代码空间
栈空间
- 调用栈
- 原始类型的数据值直接保存在栈中
- 原始类型的赋值会完整复制变量值
堆空间
- 引用类型的值存放在堆中
- 引用类型的赋值是复制引用地址
// 原始类型
function foo(){
var a = 1
var b = a
a = 2
console.log(a) // 2
console.log(b) // 1
}
foo()
// 引用类型
function foo(){
var a = {name:" 极客时间 "}
var b = a
a.name = " 极客邦 "
console.log(a) // {name:"极客邦"}
console.log(b) // {name:"极客邦"}
}
foo()
内存模型下的闭包
- 产生闭包的核心步骤
- 预扫描内部函数
- 把内部函数引用的外部变量保存到堆中
closure
的对象里
三. 垃圾数据回收
手动回收:分配内存、销毁内存都是由代码控制(如C++语言),容易造成内存泄漏
自动回收:垃圾数据由垃圾回收器来释放(如JS)
1. 调用栈中的数据回收
ESP:记录当前执行状态的指针
function foo(){
var a = 1
var b = {name:" 极客邦 "}
function showName(){
var c = " 极客时间 "
var d = {name:" 极客时间 "}
}
showName()
}
foo()
说明:
执行到showName函数时,这一函数的执行上下文压入栈,并且ESP指向showName函数的执行上下文。
当showName函数执行完成后,ESP向下移动到foo函数的执行上下文,showName函数的执行上下文虽然仍旧被保存在栈内存中,但已经是无效内存,可以被覆盖。
总结:JavaScript 引擎会通过向下移动 ESP 来销毁该函数保存在栈中的执行上下文
2堆中的数据回收
- 需要使用垃圾回收器
- 以 JavaScript 引擎 V8 为例
代际假说:
- 大部分对象在内存中存在的时间很短,简单来说,就是很多对象一经分配内存, 很快就变得不可访问
- 不死的对象,会活得更久
- V8把堆分为两个区域
- 新生代:存放生存时间短的对象,1~8M的容量,使用副垃圾回收器
- 老生代:存放生存时间久的对象,容量很大,使用主垃圾回收器
垃圾回收器工作流程
- 标记空间中活动对象和非活动对象。
- 活动对象就是还在使用的对象
- 非活动对象就是可以进行垃圾回收的对象。
- 回收非活动对象所占据的内存。其实就是在所有的标记完成之后,统一清理内存中所有被标记为可回收的对象。
- 内存整理。当内存中出现了大量的内存碎片之后,如果需要分配较大连续内存的时候,就有可能出现内存不足的情况。所以最后一步需要整理这些内存碎片。
- 内存碎片:频繁回收对象后,内存中出现的大量不连续空间
- 这步可选,因为有的垃圾回收器不会产生内存碎片,比如副垃圾回收器
1. 副垃圾回收器
Scavenge 算法:把新生代空间对半划分为两个区域,一半是对象区域,一半是空闲区域。
新加入的对象都会存放到对象区域,当对象区域快被写满时,就需要执行一次垃圾清理操作
-
流程:
-
- 对对象区域中的垃圾做标记
- 垃圾清理阶段,副垃圾回收器会把这些存活的对象复制到空闲区域中,同时它还会把这些对象有序地排列起来
- 完成复制后,对象区域与空闲区域进行角色翻转,也就是原来的对象区域变成空闲区域,原来的空闲区域变成了对象区域。
-
-
说明:
- 第2步完成了内存整理,复制后空闲区域没有内存碎片
- 第3步能让新生区的两块区域无限重复使用
- 对象晋升策略:经过两次垃圾回收依然还存活的对象, 会被移动到老生区中。
2. 主垃圾回收器
标记 - 清除算法:标记垃圾数据后直接进行清除
标记 - 整理算法:让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存
-
流程:
-
- 标记过程阶段。标记阶段就是从一组根元素开始,递归遍历这组根元素,在这个遍历过程中,能到达的元素称为活动对象,没有到达的元素就可以判断为垃圾数据
- 垃圾的清除过程。执行标记-整理算法,清除垃圾数据
-
-
说明:
- 标记-清除算法会产生大量不连续内存碎片,所以采用标记-整理算法
- 增量标记算法:V8 将标记过程分为一个个的子标记过程,同时 让垃圾回收标记和 JavaScript 应用逻辑交替进行,直到标记阶段完成。为了降低垃圾回收造成的全停顿
全停顿:执行垃圾回收算法时都将正在执行的 JavaScript 脚本暂停下来,待垃圾回收完毕后再恢复脚本执行。
学习资料
李兵:《12丨栈空间和堆空间:数据是如何存储的?》
《13丨垃圾回收:垃圾数据是如何自动回收的?》