JS性能优化

概念

内存管理

开发者主动去申请、使用、释放内存空间。

JS中的垃圾

  • 不再被引用的对象
  • 不能从根上访问到的对象

可达对象

  • 能被访问到的对象就是可达对象
  • 可达的标准是从根出发是否能被找到
  • JS中的根可以理解为全局变量对象,即全局执行上下文

GC

垃圾回收机制的简写,GC中的垃圾回收器的工作内容就是找到内存中的垃圾,并释放和回收空间。

  • GC中的垃圾:
    程序中不再需要使用的对象,和不能再访问到的对象
  • GC算法:
    查找垃圾和释放、回收空间所遵循的规则
  • GC的目的:
    实现内存空间的合理使用和良性循环

常见GC算法

  • 引用计数
  • 标记清除
  • 标记整理
  • 分代回收

引用计数算法

原理:
当对象的引用关系发生改变时,通过引用计数器修改当前对象的引用数值,当引用数为0时,就对当前的对象空间进行回收和释放。

优点:

  • 发现垃圾立即回收
  • 最大限度减少程序暂停/卡顿

缺点:

  • 无法回收循环引用的对象
  • 时间和资源开销大

标记清除算法

原理:
1.遍历所有对象,标记活动对象(可达对象)
2.遍历所有对象,清除没有标记的对象,并把上一轮遍历中作的标记清除掉

优点:

  • 能够回收循环引用的对象

缺点:

  • 空间碎片化,浪费空间
  • 不会立即对垃圾对象进行回收

标记整理算法

原理:

  • 标记清除算法的增强
  • 标记阶段的操作和标记清除一致
  • 在清除阶段会先进行整理,移动对象位置,使空间连续

优点:

  • 减少碎片化空间

缺点:

  • 不会立即回收垃圾对象

V8引擎

  • 主流的JS执行引擎
  • 采用即时编译,速度快
  • 内存设限,32位操作系统上限为800M,64位操作系统为1.5G

V8垃圾回收策略:

分代回收思想

将内存分为新生代和老生代对象存储区,针对不同对象采用不同算法。

常用GC算法

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

V8内存分配

  • V8内存空间一分为二
  • 小空间用于存储新生代对象(32M|16M)
  • 新生代指存活时间较短的对象(如局部作用域中的变量)
  • 老生代存储区(1.4G|700M)
  • 老生代对象指存活时间较长的对象,如全局作用域和闭包中的变量

V8回收新生代对象

算法:空间复制+标记整理

新生代存储区分为两个等大小空间,使用空间为From,空闲空间为To,活动对象存储在From空间,标记整理后将活动对象拷贝至To,然后From和To交换空间,From成为To,To成为From。

晋升

拷贝过程中,可能会出现晋升操作,也就是将新生代对象移动至老生代存储区。

需要晋升的情况:

  • 一轮GC之后还存活的新生代对象
  • 拷贝时To空间的使用率超过25%

V8回收老生代对象

算法:标记清除+标记整理+标记增量

主要使用标记清除完成垃圾空间的回收;当新生代对象需要晋升,但老生代存储区的空间不足时,采用标记整理进行空间优化;增量标记用来进行效率优化。

标记增量如何优化垃圾回收

垃圾回收时会阻塞JS程序的运行,标记增量将一段完整的垃圾回收操作进行分段处理,和JS程序交替进行,减少了程序被阻塞的时间。

与新生代对比

  • 新生代区域垃圾回收牺牲空间(总有一个空闲空间被浪费)换时间
  • 老生代区域空间大,变量多,不适合复制算法

Performance工具

常见的内存问题

  • 页面延迟加载或卡顿-频繁GC
  • 页面持续性出现糟糕的性能-内存膨胀
  • 页面性能随着时间越来越差-内存泄漏

监控内存的几种方式

  • 浏览器任务管理器:查看网页DOM内存和JS内存是否异常,但无法定位问题。
  • Timeline时序图记录:通过控制台-性能工具,录制时序图。
  • 堆快照查找分离DOM:通过控制台-内存工具,获取堆快照
    • 分离DOM:从DOM树上脱离(页面中不显示),但依然被引用的DOM。
  • 判断是否存在频繁GC:
    • Timeline中频繁地上升下降
    • 任务管理器中的内存频繁地增大减小

几种优化方式:

慎用全局变量

尽量使用局部变量

缓存全局变量

将使用中不可避免的全局变量缓存到局部

通过原型添加方法

在原型对象上新增实例对象需要的方法,而不是在构造函数中

避开闭包陷阱

闭包中产生的引用关系在不使用的时候要把它释放掉,使内存得以回收

避免属性访问方法的使用

直接通过对象属性名来访问对象的成员,而不是封装一个方法来访问。

function Person(){
    this.name = 'Tom'
    this.age = 18
    this.getAge = function(){
        return this.age
    }
}
const p = new Person
const pAge = p.age
// const pAge = p.getAge()

For循环优化

const arr = [1, 3, 5, 7, 9]
for(let i=0, len=arr.length; i<len; i++){
    console.log(arr[i])
}
采用最优循环方式

forEach > 优化后的for > for…in

文档碎片优化节点添加

document.createDocumentFragment()

克隆优化节点操作

domEle.cloneNode(false)

直接量(字面量)替换new Object

let arr = [1,2,3]
// 而不是
let arr2 = new Array(3)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值