浅谈垃圾回收


不论哪个垃圾回收算法,都有一套共同流程:

  1. 标记内存空间中的活动对象(在使用中的对象)和非活动对象(可以回收的对
    象)。
  2. 删除非活动对象,释放内存空间。
  3. 整理内存空间,避免频繁回收后产生的大量内存碎片(不连续内存空间)

一、引用计数

1.策略

跟踪每个变量被使用的次数

2.工作流程
  1. 当声明了⼀个变量且将⼀个引用类型赋值给该变量时,这个值的引⽤次数为1
  2. 若同⼀个值又被赋给另⼀个值,引用数 +1
  3. 如果该变量的值被其他的值覆盖了,则引用次数减 1
  4. 当这个值的引用次数变为 0 的时候,说明没有变量在使用,这个值没法被访问了,回收空间,垃圾回收器会在运行的时候清理掉引用次数为 0 的值占用的内存
3.缺点

需要⼀个计数器,所占内存空间大,因为我们也不知道被引用数量的上限。
解决不了循环引用导致的无法回收问题。

二、标记清除

1.工作流程
  1. 垃圾收集器在运行时给内存变量加上 标记,假设内存汇总所有对象都是垃圾,全标记
    为0
  2. 从根部出发,寻找可到达的变量,并将其标记清除,改为1
  3. 留有标记的变量就是待删除的,即标记为0,销毁并回收它们占⽤的内存
  4. 把所有内存中对象标记修改为0,等待下⼀轮垃圾回收。
2.优点

实现简单,⼀位⼆进制位就可以为其标记

3.缺点

内存碎片化,空闲内存块不连续
分配速度慢,因为即使使用first-fit策略,其操作仍是⼀个O(n)的操作,最坏情况是每
次都要遍历到最后,因为碎片化,大对象的分配速率会更慢。

三、复制算法

1.工作流程
  1. 将整个空间平均分成 from 和 to 两部分。
  2. 先在 from 空间进行内存分配,当空间被占满时,标记活动对象,并将其复制到 to
    空间。
  3. 复制完成后,将 from 和 to 空间互换。
2.优点

由于直接将活动对象复制到另⼀半空间,没有了清除阶段的开销,所以能在较短时间内完成
回收操作,并且每次复制的时候,对象都会集中到⼀起,相当于同时做了整理操作,避免了
内存碎片的产生。

3.缺点

首先,复制操作需要时间成本的,若堆空间很大且活动对象很多,则每次清理时间会
很久;其次,将空间⼆等分的操作,让可用的内存空间直接减少了⼀半。

四、标记整理

1.工作流程
  1. 从⼀个 GC root 集合出发,标记所有活动对象。
  2. 将所有活动对象移到内存的一端,集中到⼀起。
  3. 直接清理掉边界以外的内存,释放连续空间
2.优点

该算法既避免了标记-清除法产生内存碎片的问题,又避免了复制算法导致可用内存空间减 少的问题。

3.缺点

由于其清除和整理的操作很麻烦,甚⾄需要对 整个堆做多次搜索,故而堆越大,耗时越多。

造成内存泄漏的原因

  1. 意外的全局变量
  2. 被遗忘的定时器和回调函数
  3. 事件监听没有移除
  4. 没有清理的DOM 引用
  5. 子元素存在的内存泄漏
  6. 闭包

V8对GC优化

1.栈中数据回收

执行状态指针 ESP 在执行栈中移动,移过某执行上下文,就会被 销毁。

2.堆中数据回收

V8的GC策略基于分代式垃圾回收机制,将堆内存分为新生代老生代两区域,采用不同的垃圾回收策略进行垃圾回收。

新生代

采用 scavenge 算法处理:将新生代空间分为两半,以半空闲,一半存对 象,对对象区域做标记,存活对象复制排列到空闲区域,没有内存碎片,完成后, 清理对象区域,角色反转;

  • 当⼀个对象经过多次复制后依然存活,它将会被认为是⽣命周期较长的对象,随后会被移动 到老生代中,采用老生代的垃圾回收策略进行管理。
  • 另外还有⼀种情况,如果复制⼀个对象到空闲区时,空闲区空间占用超过了 25%,那么这 个对象会被直接晋升到老生代空间中,设置为 25% 的比例的原因是,当完成 Scavenge 回收 后,空闲区将翻转成使用区,继续进行对象内存的分配,若占比过大,将会影响后续内存分配。
老生代

老生代中的对象⼀般存活时间较长且数量也多,使用了两个算法,分别是标记清除算法标记压缩算法

  • 首先是标记阶段,从⼀组根元素开始,递归遍历这组根元素,遍历过程中能到达的元素称为 活动对象,没有到达的元素就可以判断为非活动对象 。
  • 清除阶段老生代垃圾回收器会直接将非活动对象清理掉 。
  • 采用标记压缩算法整理内存。

在老生代中,以下情况会先启动标记清除算法:

  • 某⼀个空间没有分块的时候
  • 空间中被对象超过⼀定限制
  • 空间不能保证新生代中的对象移动到老生代中

由于JS是单线程运行的,意味着垃圾回收算法和脚本任务在同⼀线程内运行,在执
行垃圾回收时,后续脚本任务需要等垃圾回收完成后才能继续执行。若堆中的数据
量非常大,⼀次完整垃圾回收的时间会非常长,导致应⽤的性能和响应能力直线下
降。为了避免垃圾回收影响应用的性能,V8 将标记的过程拆分成多个子标记,让垃
圾回收标记和应⽤逻辑交替执行,避免脚本任务等待较长时间

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值