垃圾回收是回收哪里的数据
通常我们所说的垃圾回收是针对堆内存中的数据
代际假说
- 代际假说是垃圾回收领域的一个重要术语
特点
- 大部分对象在内存中存在的时间很短(很多对象一经分配内存,很快就变得不可访问)
- 不死的对象,会活的更久(window,DOM,web api)
V8的优化策略
- 把堆分为新生代和老生代两个区域
- 新生代:通常支持1-8M
- 老生代:比较大
- 对于这两块区域,V8分别使用不同的垃圾回收器
- 副垃圾回收器(新生代垃圾回收器)
- 主垃圾回收器(老生代垃圾回收器)
垃圾回收器的工作流程
- 标记活动空间中活动对象和非活动对象。活动对象就是还在使用的对象,非活动对象就是可以进行垃圾回收的对象
- 回收非活动对象所占的内存。其实就是在标记完成之后,统一清理内存中所有被标记为可回收的对象
- 内存整理
- 频繁回收对象后,内存中存在大量不连续空间,这种不连续内存空间称为内存碎片
- 当出现大量内存碎片后,当需要分配较大连续内存时,可能会出现内存不足的情况
- 所以最后一步需要整理这些内存碎片(可选),副垃圾回收器不会产生内存碎片
副垃圾回收器
主要负责新生区的垃圾回收。小对象会被分配到新生区。
新生区
- 使用Scavenge算法,把新生区分为两个区,一半是对象区,一半是空闲区
回收流程
- 对对象区域中的垃圾做标记
- 垃圾清理阶段,副垃圾回收器把这些存活对象复制到空闲区中,同时还会把这些对象有序地进行排列,复制的过程就相当于完成了内存整理操作,所以复制后空闲区就没有内存碎片了
- 复制完成后,对象区与空闲区进行角色翻转。原来的对象区变成了空闲区,空闲区变成了对象区,这样就完成了垃圾对象回收操作
- 这种角色翻转的操作还能让新生代中的这两块区域无限重复使用下去
对象晋升策略
- 每次执行清理操作是,都需要将存活的对象从对象区复制到空闲区
- 复制操作需要时间成本,如果新生区空间设置太大,每次清理时间就会过久
- 所以为了执行效率,新生区空间一般设置比较小
- 由于空间小,所以容易被存活对象装满
- JavaScript采用对象晋升策略,经过两次垃圾回收依然还存活的对象,会被移动到老生区
主垃圾回收器
主要负责老生区的垃圾回收。除了新生区中晋升的对象,一些大的对象会直接被分配到老生区。
老生区
- 对象占用空间大,存活时间长
- 由于对象比较大,采用Scavenge算法进行垃圾回收,复制这些大的对象会花费较多时间,导致回收执行效率不高,同时还会浪费一半的空间,所以主垃圾回收器采用**标记-清除(Mak-Sweep)**算法进行垃圾回收
标记-清除
- 标记:从一组根元素开始,递归遍历这组根元素,遍历过程中,能到达的元素称为活动对象,没有到达的元素判断为垃圾数据
- 清除
问题
- 多次执行标记-清除算法后,产生大量不连续的内存碎片,导致大对象无法分配到足够的连续内存,于是产生了另外一种算法标记-整理
标记-整理
- 标记:与标记-清除算法一样
- 整理:让所有存活对象都向一端移动,清理掉端边界以外的内存