javaScript的垃圾回收

129 阅读5分钟

JavaScript的内存管理

  • 申请内存空间
  • 使用内存空间
  • 释放内存空间

JavaScript中的垃圾

  • 对象不再被引用的时候是垃圾
  • 对象不能从根上访问(可达对象)到时是垃圾

JavaScript中的可达对象

  • 可以访问到的对象就是可达对象(引用、作用域链)
  • 可达的标准是从根(全局变量)出发是否能被找到

GC是什么

GC就是垃圾回收机制(Garbage Collection)的缩写。GC可以找到内存中的垃圾,释放并回收空间。

GC算法是什么

GC算法就是GC在工作时,查找和回收时所遵循的规则

常见的GC算法

  • 引用计数算法
  • 标记清除算法
  • 复制算法(主要运用在新生代对象中)
  • 标记整理算法(主要运用在老生代对象中)
  • 分代回收算法

一、引用计数算法:

原理: 设置引用数,它会在每个对象上记录被引用的次数,引用一次该对象就+1,取消引用就-1,直到该对象的引用次数为0时,则立即进行回收对象

优点: ①发现垃圾时可以立即进行回收 ②可以最大限度的减少程序的暂停情况

缺点: 无法回收循环引用的对象,耗时长

// 循环引用
let obj1 = {name: 'obj1'}
let obj2 = {name: 'obj2'}
obj1.child = obj2
obj2.child = obj1

二、标记清除算法:

原理: 分为标记和清除两个阶段。①先遍历所有对象标记所有的活动对象 ②再遍历所有对象把没有标记的对象进行清除,回收相应的空间。顺便把已经标记过的对象取消标记,方便下一轮GC操作

优点: ①可以回收循环引用的对象 ②不会移动对象,与保守式GC算法兼容

缺点: ①空间碎片化,会导致即便总空间够用,但是因为碎片化,每一个小空间有可能不足以存放,或者存放后会浪费一些 。 ②效率低,时间长,因为垃圾回收时会停止程序的运行,所以会导致用户体验差 ③不会立即回收垃圾对象

三、复制算法:

原理: 将内存分为两块,每次只用其中一块,当这一块内存用完,就将还活着的对象复制到另外一块上面。复制算法不会产生内存碎片。它在V8的新生代存储中使用过。

优点: 没有内存碎片

缺点: 耗内存空间,需要2倍的空间

四、标记整理算法:

原理: 标记整理可以看成是标记清除算法的增强。①先遍历所有对象标记所有的活动对象 ②再遍历所有对象把没有标记的对象进行整理,移动对象的位置 ③进行清除,回收

优点: 减少碎片化空间

缺点: ①移动对象位置,效率慢 ②不会立即回收垃圾对象

认识V8

  • V8是一个主流的JavaScript执行引擎
  • V8采用即时编译
  • V8内存有限制。一般新生代对象存储区(32M-64位系统 | 16M-32位系统),老生代对象存储区(1.4G-64位系统 | 700M-32位系统)

V8垃圾回收策略

  • 采用分代回收的思想
  • 内存分为新生代存储区和老生代存储区
  • 针对不同存储区对象采用不同的回收算法

V8采用的GC算法

  • 分代回收
  • 复制算法
  • 标记清除算法
  • 标记整理算法
  • 标记增量算法

V8的内存两大区域:新生代和老生代区域

新生代对象:

  • 新生代对象是指:存活时间较短的对象。它回收过程采用的是复制算法和标记整理来实现的。
  • 新生代存储区分为了两个等大的空间,一个是使用空间(From),一个是空闲空间(To),所有的活动对象都存放在From空间。 当触发垃圾回收时,它会将From空间的活动对象进行标记整理后拷贝到To空间。然后清除From空间的活动对象。
  • 其他细节:在From拷贝到To空间的过程中,可能会出现新生代对象晋升到老生代对象的情况。出现这个情况的原因有:
  1. 新生代对象经过一轮垃圾回收后,还存在着,则会晋升到老生代对象
  2. 当To空间的使用率超过25%的时候,会把所有活动对象晋升到老生代对象

新生代区域垃圾回收是使用空间来换取时间

老生代对象:

  • 老生代对象是指:存活时间较长的对象(如程序结束时才销毁的对象)。
  • 它回收过程采用的是标记清除算法 + 标记整理 + 增量标记算法来实现的。
  • 首先使用标记清除完成垃圾空间的回收
  • 再采用标记整理进行空间优化
  • 再采用增量标记进行效率优化

增量标记是如何效率优化的:

原理: 在一段程序执行过程中,如果发生了垃圾回收,则去遍历所有对象,对其中的部分对象进行标记清除,然后再继续执行代码,然后过一段时间后进行剩余的部分回收对象进行标记清除,再继续执行代码,如此往复,直至完全清除。

优点: 降低垃圾回收时程序的卡顿时间