垃圾回收(GC)
js中的垃圾回收也叫GC
.就是找到内存中的垃圾(通过GC算法),然后对这个垃圾空间进行释放与回收.方便后续代码继续使用.
js中的内存管理是自动的,每当我们去创建一个对象的时候会自动去分配内存空间,后面通过判断确认其是否是垃圾.
当垃圾回收工作的时候,它会阻塞js代码的执行
什么是根呢?
js中的根可以理解为全局对象
,也叫全局上下文
js中的垃圾有哪些?
对象不再被引用
是垃圾
对象不能从根上访问
时是垃圾(局部作用域,闭包除外)
let obj = {name:'xm'} // 这里创建并使用了一个小明的内存空间,然后被obj引用了.而且这个空间能通过obj从全局被拿到
obj = null // 这里将obj与小明的引用断掉了,小明的空间没有其它引用所以无法从根访问到了,固然会被垃圾回收
怎么判断不是垃圾?(是可达的)
可以从根出发且能访问到
的对象就是可达对象(引用,作用域链)
引用和可达的代码体现
let obj = {name:'xm'} // 这里创建并使用了一个小明的内存空间,然后被obj引用了.小明对象能从全局访问到,那么说明小明对象是可达的
let ali = obj // 这里引用了小明的内存空间
obj = null // 这里将obj与小明的引用断掉了 但是阿狸的引用并没有被断掉 那么小明对象任然是可达的
GC里的垃圾是什么?
- 程序中不再需要使用的对象
function func(){name = 'lg'return `${name} is a coder`
}
func() // 函数调用完成之后 name不再被使用
- 程序中不能再访问的对象
function func(){const name = 'lg'return `${name} is a coder`
}
func() // 函数调用完成之后 name不再被使用 且外部无法访问
GC算法
GC是一种机制,查找垃圾\释放\回收\分配空间时所遵循的规则叫GC算法
-
引用计数
设置引用数,判断当前引用数是否为0(如果为0就是垃圾).当引用关系发生改变时修改引用数字
优点: 发现垃圾立即回收,减少程序卡顿时间
缺点:无法回收循环引用的对象
,如下:首先fn中的obj1与obj2是无法在全局上下文中访问的,但是他们的引用数不为0,所以回收不了
function fn() {const obj1 = {}const obj2 = {}obj1.name = obj2obj2.name = obj1return 'xx' }
时间开销大
(需要时刻监听引用数的变化,而且需要数值也需要耗费时间)
-
标记清除
分两个阶段进行.遍历所有可达对象标记活动对象,不可达对象无法被标记. 遍历所有对象清除没有标记的对象,回收后的空间会放到空闲链表中.完成一次垃圾回收之后清除所有标记
优点: 可以回收循环引用的对象
缺点:- 容易产生
空间碎片化
.由于垃圾对象在地址上是不连续的,所以回收后的空间也不是连续的,造成后续在使用的时候就要匹配内存大小. - 不能立即回收垃圾对象,因为它要在最后才能清除
- 容易产生
-
标记整理
可以看做是标记清除的增强版,只不过在清除阶段会先执行整理内存空间,移动对象的位置让地址产生连续.
优点: 解决了空间碎片化的问题
缺点: 不能立即回收垃圾对象
-
V8垃圾回收
认识V8
- V8是一款主流的JS执行引擎,在谷歌和node中都用的v8
- V8采用即时编译,所以很快
- V8内存设限 (本身是为浏览器而设计的,不需要太大的内存.在64位操作系统,内存在1.5G(这个内存下消耗的时间也才1秒) 32位操作系统,内存在800M.)
V8垃圾回收策略
- 基于V8内存设限,采用分代回收的思想(V8内存空间一分为二,小空间存储新生代,大空间存储老生代).针对不同对象采用不同的算法
- 按内存分为新生代和老生代
- 新生代(64位32M,32位16M,因为分配的内存比较小,所以采用一分为二的方法,用空间换时间)
- 指的是
存活时间较短
的对象(比如局部作用域中的变量) - 回收过程采用复制算法和标记整理
- 内存区分为两个相同大小的空间(From,To)
- 使用空间为From,所有活动对象存储在From空间.To是空闲空间
- 当From空间应用到一定程度之后就会触发GC操作,将活动对象进行整理拷贝至空闲空间To.这样新生代就完成了空间的释放与回收操作
- 指的是
- 老生代(64位1.4G,32位700M,内存相对比较大不需要像新生代一样一分为二)
- 指的是
存活时间较长
的对象(比如全局作用域或闭包中的变量) - 回收过程主要采用标记清除,当新生代移过来而老生代空间不够用时会采用标记整理,最后采用增量标记进行效率优化
- 指的是
标记增量如何优化垃圾回收?
让gc操作与代码交替运行,解决长时间阻塞问题
界定内存问题的标准
- 内存泄露:内存使用持续升高
- 内存膨胀(浏览器为了满足代码需求,主动申请的内存. 也有可能是设备配置的问题,带动不了. 这里要定位是设备问题还是代码问题)
- 频繁的垃圾回收(让用户感觉卡顿):Timeline中频繁的上升下降,任务管理器中数据频繁的增加减小
内存监控
- 浏览器任务管理器
- Timeline时序图记录
- 对快照查找分离DOM(分离DOM是一种内存泄露)
V8引擎工作流程(解析和编译js代码)
V8只是浏览器渲染引擎里执行js代码的一个组成部分
-
Scanner词法分析器
是一个Js代码的扫描器,用来对js代码进行词法分析,它会把代码分析成不同的tokens.如下
- Parser全解析- 解析被使用的代码
- 生成AST
- 构建具体scopes信息,变量引用,声明等
- 抛出所有语法错误
会将词法分析之后的tokens解析成AST语法树,同时在分析中对语法进行校验,如果有语法错误就会抛出错误.
-
Preparser预解析
- 跳过未被使用的代码
- 不生成AST,但是会创建作用域信息
- 依据规范抛出特定错误
- 解析速度更快
-
Ignition解释器
将生成的AST语法树转为字节码 -
TurboFan编译器
把字节码转为汇编代码(机器码),之后的话就可以开始代码的执行了