大前端-浏览器垃圾回收机制

547 阅读6分钟

内存管理

  • 内存:由可读写单元组成,表示一片可操作空间
  • 管理:人为的去操作一片空间的申请,使用和释放
  • 内存管理:开发者主动申请空间,使用空间和释放空间
  • 管理流程:申请--使用--释放

javascript中的垃圾回收

  • javascript中的垃圾回收
  1. javascript中内存管理是自动的;
  2. 对象不再被引用时是垃圾
  3. 对象不能从根上访问到时是垃圾
  • javascript中的可达对象
  1. 可以访问到的对象就是可达对象(引用,作用域链)
  2. 可达的标准就是从根处方是否能够被找到
  3. javascript中的根就可以理解为是全局变量对象

GC算法介绍

  • GC定义与作用
  1. GC就是垃圾回收机制的简写
  2. GC可以找到内存中的垃圾,并释放和回收空间
  • GC里的垃圾是什么?
  1. 程序中不再需要使用的对象
  2. 程序中不能再访问到的对象
  • GC算法是什么?
  1. GC是一种机制,垃圾回收器完成具体的工作
  2. 工作的内容就是查找垃圾释放空间,回收空间
  3. 算法就是工作时查找和回收所遵循的规则
  • 常见GC算法
  1. 引用计数
  2. 标记清除
  3. 标记整理
  4. 分代回收

引用计数算法实现原理

  • 核心思想:设置引用数,判断当前引用数是否为0;
  • 设置引用计数器;
  • 当引用关系改变时修改引用数组;
  • 引用数字为0时立即回收 引用计数算法的有点:
  • 发现垃圾时立即回收
  • 最大限度减少程序暂停 引用计数算法确定:
  • 无法回收循环引用的对象;
  • 时间开销大
//循环引用代码演示
function fn(){
    let obj1 = {};
    let obj2 = {};
    obj1.name = obj2;
    obj2.name = obj1;
}

fn();//obj1和obj2循环引用,引用计数无法回收

标记清除算法实现原理

  • 核心思想:分标记和清除两个阶段完成
  • 遍历所有对象找标记活动对象
  • 遍历所有对象清除没有标记的对象
  • 回收相应的空间

2021-03-19_231018.png

  • 优点:可以解决引用清除中循环引用的问题
  • 确定:会产生空间的碎片化,清理的空间不连续,不能达到空间的最大化使用

标记整理算法实现原理

  • 标记整理可以看做是标记清除的增强
  • 标记阶段的操作和标记清除一致
  • 清除阶段会先执行整理,移动对象位置

2021-03-19_231914.png

认识V8

  • V8是一款主流的javascript执行引擎
  • V8采用即时编译
  • V8内存设限:64位操作系统下是1.5G,32位操作系统下是800M,便于垃圾回收,垃圾回收时间更短,在浏览器环境下内存已经足够使用 V8垃圾回收策略
  • 采用分代回收的思想
  • 内存分为新生代和老生代
  • 针对不同对象采用不同算法

2021-03-20_151043.png V8中常用GC算法:

  • 分代回收
  • 空间复制
  • 标记清除
  • 标记清理
  • 标记增量

V8如何回收新生代对象

V8内存分配:

  • V8内存空间一分为二
  • 小空间用于存储新生代对象,32位操作系统下是16M,64位操作系统下是32M
  • 新生代指的是存活时间较短的对象

2021-03-20_153723.png 新生代对象回收实现:

  • 回收过程采用复制算法 + 标记整理
  • 新生代内存分为两个等大的空间
  • 使用空间为from,空闲空间为to
  • 活动对象存储于from空间
  • 标记整理后将活动对象拷贝至to
  • from和to交互空间完成释放 回收细节说明:
  • 拷贝过程中可能出现晋升
  • 晋升就是将新生代对象移动至老生代
  • 一轮GC后还存活的新生代需要晋升
  • to空间的使用率超过25%,就开始回收

V8如何回收老生代对象

老生代对象说明:

  • 老生代对象存放在右侧老生代区域
  • 64操作系统下是1.4G,32为操作系统是700M
  • 老生代对象就是指存活时间较长的对象 老生代对象回收实现:
  • 主要采用标记清除,标记整理,增量标记算法
  • 首先使用标记清除完成垃圾空间的回收
  • 采用标记整理进行空间优化
  • 采用增量标记进行效率优化 细节对比:
  • 新生代区域垃圾回收使用空间换时间
  • 老生代区域垃圾回收不适合复制算法

2021-03-20_153638.png

V8总结

  • V8是一款主流的javascript执行引擎
  • V8内存设置上限
  • V8采用基于分代回收思想实现垃圾回收
  • V8内存分为新生代和老生代

代码优化

  • javascript中的内存管理自动完成
  • 执行引擎会使用不同的GC算法
  • javascript是单线程机制的解释型语言 避免全局变量:
  1. 全局变量的特点
  • 全局变量挂载在window下
  • 全局变量至少存在一个引用计数
  • 全局变量存活更久,持续占用内存
  1. 为什么要慎用全局变量
  • 全局变量定义在全局执行上下文,是所有作用域链的顶端
  • 全局执行上下文一直存在于上下文执行栈,直到程序退出
  • 如果某一个局部作用域出现了同名变量则会遮蔽或污染全局
  1. 避免全局查找
  • 目标变量不存在于当前作用域内,同=通过作用域向上查找
  • 减少全局查找降低时间消耗
  • 减少不必要的全局变量定义
  • 全局变量数据局部化
  1. 缓存全局变量
  • 将使用中无法避免的全局变量缓存到局部;
  • 通过原型新增方法,在原型对象上新增实例对象需要的方法 避开闭包陷阱
  1. 闭包特点
  • 外包作用域可以访问内部的引用
  • 在“外”部作用域访问“内”部作用域的数据
  1. 关于闭包
  • 闭包是一种强大的语法
  • 闭包使用不当很容易出现内存泄漏
  • 不要为了闭包而闭包 避免属性访问方法的使用
  • js不需要属性的访问方法,所有属性都是外部可见的
  • 使用属性访问方法只会增加一层重定义,没有访问的控制力 其它优化:
  • 采用最优循环方式
  • for循环优化,使用变量存储数组长度
  • 节点添加优化,节点添加操作必然会有回流和重绘,使用字符串拼接DOM,批量添加,避免多次添加
  • 克隆优化节点操作
  • 使用字面量替换Object操作