引用计数工作原理和优缺点
- 引用计数工作原理:通过设置引用数,判断当前引用数是否为0,再根据引用关系改变时修改引用数字,当引用数字为0时立即进行回收。(其实就是是否被引用,如果无其他地方引用则直接进行回收,如果其他地方还在引用则不会进行回收)
- 引用计数优缺点:
- 优点:
- 发现垃圾时立即回收
- 最大限度减少程序暂停(当执行内存即将爆满时,引用计数算法是实时的监控引用数字为0对象,就会立即回收计数为0对象,并且释放内存)
- 缺点:
- 无法回收循环引用的对象
- 时间开销大(由于监控当期引用数值时刻的变化)
- 优点:
function fn(){
const obj1={}
const obj2={}
obj1.name=obj2
obj2.name=obj1
return '11111'
}
fn()
循环引用:由于fn函数再运行完成后,会进行回收,当回收obj1时,发现obj1被引用,导致无法进行回收;当回收obj2时发现obj2被引用,导致无法进行回收
标记整理算法的工作流程
标记清除算法实现原理:
- 核心思想:分标记和清除二个阶段完成 a.遍历所有对象找到标记活动对象;第一阶段 b.遍历所有对象清除没有标志对象(会抹掉第一阶段的标记对象的标记便于GC操作下次正常工作);第二阶段 c.回收相应的空间
- 标记清除算法优缺点
- 优点:解决循环引用无法回收
- 缺点:形成空间碎片化(由于回收时垃圾对象在地址上时是不连续的,在进行回收后,导致释放的空间存在各个地方,在使用的时候,有时无法按照要求进行匹配,新进的对象会大小不一,不能使内存空间最大化使用)
标记整理算法实现原理:
- 标记整理可以看做是标记清除的增强
- 标记阶段的操作和标记清除一致
- 清除阶段会先执行整理,移动对象位置 执行标记操作时会把活动的对象进行标记,再进行整理操作,进行位置改变,会将活动对象进行移动,将地址变成连续,将非活动对象和空空间进行回收
v8垃圾回收
v8垃圾回收策略
- 采用分代回收思想(将内存空间分成新生代、老生代)
- 针对不同对象(新生代和老生代)采用不同算法(新生代对象存储采用具体的GC算法;老生代对象存储采用具体算法)
v8中常见GC算法
- 分代回收
- 空间复制
- 标记清除
- 标记整理
- 标记增量
新生代对象回收
- 新生代存储空间为(32M|16M)
- 新生代指的是存活时间较短的对象
新生代对象回收实现
- 新生代对象回收过程采用复制算法+标记整理
- 新生代内存区分为二个等大小的空间
- 使用空间为From,空闲空间为To
- 代码在执行时需要申请空间使用首先会将所有的变量对象分配到from空间中
- 标记整理后将活动对象拷贝至To
- From与To交换空间完成释放(释放所有的From空间)
回收细节说明
- 拷贝过程中可能出现晋升(拷贝时发现某一个变量对象所使用的空间(新生代对象中的变量)在老生代对象中也出现)
- 晋升就是将新生代对象移动至老生代
- 一轮GC还存活的新生代需要晋升(判断依据)
- To空间的使用率超过25%(判断依据)
v8回收老生代对象
- 64位操作系统中为1.4G,32位操作系统700M
- 老生代对象就是指存活时间较长的对象(例如在全局作用域、闭包中的变量)
老生代对象回收实现
主要采用标记清除、标记整理、增量标记算法
- 首先使用标记清除完成垃圾空间的回收
- 采用标记整理进行空间优化(什么时候使用标记整理:如果发现新生代存储区域的内容往老生代存储区域移动时,发现老生代存储的空间无法满足需要移动的新生代存储对象)
- 采用增量标记进行效率优化
标记增量如何优化垃圾回收
- 增量标记算法在代码执行完成后,垃圾回收和程序运行交替
- 工作原理:先找到第一层的可达对象进行标记,再次让程序运行,找到第二层可达对象后,程序停止运行,进行标记,依次进行交替执行,当所有的可达对象标记完成后,程序最后一次执行,进行清除操作