一 目录
不折腾的前端,和咸鱼有什么区别
目录 |
---|
一 目录 |
二 前言 |
三 栈垃圾回收 |
四 堆垃圾回收 |
五 新生代和老生代 |
5.1 新生代 – 副垃圾回收器 |
5.2 老生代 – 主垃圾回收器 |
六 全进展 |
七 参考文献 |
二 前言
返回目录
原始数据类型是存储在栈空间中的,援用类型的数据是存储在堆空间中的。
不过有些数据被应用之后,可能就不再须要了,咱们把这种数据称为垃圾数据。
如果这些垃圾数据始终保留在内存中,那么内存会越用越多。
所以咱们须要对这些垃圾数据进行回收,以开释无限的内存空间。
这时候就引入了垃圾回收机制。
垃圾回收策略分为 手动回收 和 主动回收 两种:
- 手动回收:C/C++ 语言能够通过
free()
这些代码来决定何时分配内存、何时销毁内存。 - 主动回收:JavaScript、Python、Java 等产生的垃圾数据是由垃圾回收器来开释的。
三 栈垃圾回收
返回目录
当函数执行完结,JavaScript 引擎通过向下挪动 ESP
指针(记录调用栈以后执行状态的指针),来销毁该函数保留在栈中的执行上下文(变量环境、词法环境、this
、outer
)。
如果有代码:
function foo(){
var a = 1;
var b = {name: 'jsliang'};
function showName(){
var c = 2;
var d = {name: 'zhazhaliang'};
}
showName();}
foo();
在下面代码中,依据浏览器策略:
- 先到全局执行上下文
- 再到
foo
函数执行上下文 - 最初到
showName
函数执行上下文
依据栈的个性,此时 JavaScript 状况应为:
调用栈 |
---|
showName 函数执行上下文 |
foo 函数执行上下文 |
全局执行上下文 |
而后开始开释过程:
- 执行到
showName
函数的时候,创立showName
函数的执行上下文,并将showName
函数的执行上下文压入栈中。 - 以此同时,咱们会有一个记录以后执行状态的指针(称为
ESP
),指向调用栈中showName
函数的执行上下文,示意以后正在执行showName
函数。 - 当
showName
函数执行结束,函数执行流程进入foo
函数,这时候 ESP 就下移到foo
函数的执行上下文。这个下移操作就是销毁showName
函数执行上下文的过程。
如果 showName
函数上面还调用了另一个函数,那么它会笼罩 showName
函数的内容,用来寄存另一个函数的执行上下文。
就是说:当一个函数执行完结之后,JavaScript 引擎会通过向下挪动 ESP
来销毁保留栈中的执行上下文。
四 堆垃圾回收
返回目录
在下面,咱们将栈中的垃圾通过 ESP
进行回收了:
function foo(){
var a = 1;
var b = {name: 'jsliang'};
function showName(){
var c = 2;
var d = {name: 'zhazhaliang'};
}
showName();}
foo();
然而,外面的 b
和 d
其实是仍旧存在于堆中的。
要回收对象的垃圾数据,就须要 JavaScript 中的垃圾回收器了。
五 新生代和老生代
返回目录
代际假说特点:
- 大部分对象存活工夫很短
- 不被销毁的对象,会活的更久
这两个特点不仅仅实用于 JavaScript,同样实用于大多数的动静语言,如 Java、Python 等。
V8 中会把堆分为 新生代 和 老生代 两个区域,新生代中寄存的是生存工夫短的对象,老生代中寄存的生存工夫久的对象。
新生区的容量没有老生区那么大,所以 V8 别离应用 2 个不同的垃圾回收器,来高效施行垃圾回收:
- 副垃圾回收器。次要负责新生代的垃圾回收。
- 主垃圾回收器。次要负责老生代的垃圾回收。
5.1 新生代 – 副垃圾回收器
返回目录
算法:Scavenge 算法
对象区域 | 闲暇区域 |
---|---|
… | … |
原理:
- 把新生代空间对半划分为两个区域,一半是对象区域,一半是闲暇区域。
- 新退出的对象都会寄存到对象区域,当对象区域快被写满时,就须要执行一次垃圾清理操作。
- 先对对象区域中的垃圾做标记,标记实现之后,把这些存活的对象复制到闲暇区域中
- 实现复制后,对象区域与闲暇区域进行角色翻转,也就是原来的对象区域变成闲暇区域,原来的闲暇区域变成了对象区域。
对象降职策略:通过两次垃圾回收仍然还存活的对象,会被挪动到老生区中。
5.2 老生代 – 主垃圾回收器
返回目录
- 算法:标记 – 革除(Mark-Sweep)算法
- 原理:
- 标记:标记阶段就是从一组根元素开始,递归遍历这组根元素,在这个遍历过程中,能达到的元素称为流动对象,没有达到的元素就能够判断为垃圾数据。
- 革除:将垃圾数据进行革除。
对一块内存屡次执行标记 – 革除算法后,会产生大量不间断的内存碎片。而碎片过多会导致大对象无奈调配到足够的间断内存。
这时候就须要标记整顿。
- 算法:标记 – 整顿(Mark-Compact)算法
- 原理:
- 标记:和标记 – 革除的标记过程一样,从一组根元素开始,递归遍历这组根元素,在这个遍历过程中,能达到的元素标记为流动对象。
- 整顿:让所有存活的对象都向内存的一端挪动
- 革除:清理掉端边界以外的内存
六 全进展
返回目录
因为 JavaScript 是单线程的,所以一旦执行垃圾回收算法,那正在执行的 JavaScript 脚本须要暂停下来,等垃圾回收结束之后再复原脚本执行。
这种行为叫 全进展(Stop-The-World)。
如果堆中的数据较多,那么回收须要工夫,会造成页面卡顿状态,所以为了升高这个卡顿,V8 将标记过程分为一个一个子标记过程,同时垃圾回收标记和 JavaScript 应用逻辑交替进行。
- 优化算法:增量标记(Incremental Marking)算法
- 原理:
- 为了升高老生代的垃圾回收而造成的卡顿
- V8 把一个残缺的垃圾回收工作拆分为很多小的工作
- 让垃圾回收标记和 JavaScript 应用逻辑交替进行
七 参考文献
返回目录
- JavaScript 进阶 - 内存机制(表情包初探)【浏览倡议:20min】
jsliang 的文档库由 梁峻荣 采纳 常识共享 署名 - 非商业性应用 - 雷同形式共享 4.0 国内 许可协定 进行许可。<br/> 基于 https://github.com/LiangJunrong/document-library 上的作品创作。<br/> 本许可协定受权之外的应用权限能够从 https://creativecommons.org/licenses/by-nc-sa/2.5/cn/ 处取得。