JavaScript 性能优化


简单概要

  • 内存管理
  • 垃圾回收与常见 GC 算法
  • V8 引擎的垃圾回收
  • Performance 工具
  • 代码优化栗子

内存管理

简单叙述内存,下面是一个小 code
function fn() {
    arr = []
    arr[1000000] = 'hellow'
}
fn()
// 如果不是很了解内存机制,就有可能编写出这样的代码,内存是持续升高,并没有回落,蓝色线条

image

内存管理介绍

  • 内存:由可读写单元组成,表示一片可操作空间
  • 管理:人为的去操作一片空间的申请、使用和释放
  • 内存管理:开发者主动申请空间、使用空间、释放空间
  • 管理流程:较为简单,申请-使用-释放、
// 申请
let obj = {}
// 使用
obj.name = 'zhang'
// 释放
obj = {}

垃圾回收

  • JavaScript 中内存管理是自动的
  • 对象不再被引用时是垃圾
  • 对象不能从根上访问到时是垃圾
实际上 JavaScript 的垃圾回收其实就是找到这个垃圾,让 JavaScript 的执行引擎来进行空间的释放和回收。

GC 垃圾是什么

  • GC就是垃圾回收机制的简写
  • GC可以找到内存中的垃圾、并释放和回收空间
  • 程序中不再需要使用的对象以及程序中不能再访问到的对象就是 GC 的垃圾

GC 算法是什么

  • GC是一种机制,垃圾回收器完成具体的工作
  • 工作的内容就是查找垃圾释放空间、回收空间
  • 算法就是工作时查找和回收所遵循的规则

常见 GC 算法

  • 引用计数
  • 标记清除
  • 标记整理
  • 分代回收
一、引用计数算法。 核心思想:设置引用数,判断当前引用数是否为0 引用计数器 引用关系改变时修改引用数字 引用数字为0时立即回收
const user1 = {age: 11}
const user2 = {age: 22}
const user3 = {age: 33}

const nameList = [user1.age, user2.age, user3.age]

function fn(){
    const num1 = 1
    const num2 = 2
}

fn()
当上述代码 fn() 执行完毕后,由于 num1 和 num2 加上了 const 声明,所以外部访问不到,这时 num1 和 num2 身上的引用计数会变成 0,当引用计数为 0时,GC就会把他当成垃圾回收掉,而 user123,因为被数组 nameList 引用着,所以不会被回收。 引用计数算法优点:【发现垃圾时立即回收】【最大限度减少程序暂停】 缺点:【无法回收循环引用的对象】【时间开销大】 二、标记清除算法。 核心思想:分标记和清除两个阶段完成 阶段一:遍历所有对象找标记活动对象 阶段二:遍历所有对象清除没有标记对象 回收相应的空间
标记清除算法优点:【可以解决循环引用不能回收问题】 缺点:【会产生空间碎片化问题,不能让空间得到最大化使用】

V8 引擎

  • V8 是一款主流的 JavaScript 执行引擎
  • V8 采用即时编译
  • V8 内存设限

垃圾回收策略

  • 采用分代回收
  • 内存分为新生代、老生代
  • 针对不同对象采用不同算法
回收策略图

image

以及 V8 常用的 GC 算法,因为 V8 是设有内存上限,所以采用分代回收思路,不同代的对象采用更适合的 GC 算法
  • 分代回收
  • 空间复制
  • 标记清除
  • 标记整理
  • 标记增量

V8 内存分配

  • V8 内存空间一分为二
  • 小空间用于存储新生代对象(32M|16M)
  • 新生代指的是存活时间较短的对象

V8 回收新生代对象

  • 回收过程采用复制算法 + 标记整理
  • 新生代内存区分为二个等大小空间
  • 使用空间为 From,空闲时间为 To
  • 活动对象存储于 From 空间
  • 标记整理后将对象拷贝至 To
  • From 与 To 交换空间完成释放

V8 老生代对象存放区域

  • 老生代对象存放在右侧老生代区域
  • 64 位操作系统 1.4 G,32 操作系统 700M
  • 老生代对象就是指存活时间较长的对象

老生代对象回收实现

  • 主要采用标记清除、标记整理、增量标记算法
  • 首先使用标记清除完成垃圾空间的回收
  • 采用标记整理进行空间优化
  • 采用增量标记进行效率优化

Performance 监控面板

  • 可以使用 chrome 的开发人员工具面板,选择性能
  • 开启录制功能,访问具体界面
  • 执行用户行为,一段时间后停止录制
  • 分析界面中记录的内存信息
一、内存问题的外在表现(假设网络情况正常)
  • 页面出延迟加载或经常性暂停(可能伴随着频繁垃圾回收出现,在于有些代码瞬间让程序爆炸)
  • 页面持续性出现糟糕的性能(内存膨胀,指的是当前界面为了达到最近使用速度,会申请内存空间,但是这个内存空间大小超过了设备本身所提供的大小)
  • 页面的性能随时间延迟越来越差(内存泄露)
二、监控内存的几种方式
  • 浏览器任务管理器
  • Timeline 时序图记录
  • 堆快照查找分离 DOM
  • 判断是否存在频繁的垃圾回收