JavaScript性能优化详解

性能优化介绍

  • 性能优化是不可避免的
  • 无处不在的前端性能优化
function func(){
   
	arr = []
	arr[100000] = 'lg is a dog'
}
func()

JavaScript语言的优化

1、内存管理

内存管理介绍

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

2、垃圾回收与常见GC算法

垃圾回收
JavaScript中的垃圾:

  • JavaScript中内存管理是自动的
  • 对象不再被引用时是垃圾
  • 对象不能从根上访问到时是垃圾
    JavaScript中的可达对象:
  • 可以访问到的对象就是可达对象(引用、作用域链)
  • 可达的标准就是从根出发是否能够被找到
  • JavaScript中的根就可以理解为是全局变量对象
//引用
let obj = {
   age: 34}
let obj2 = obj
obj = null
//可达
function objGroup(obj1,obj2){
   
	obj1.next = obj2
	obj2.prev = obj1
	return {
   
		o1: obj1,
		o2:obj2
	}
}
let obj = objGroup({
   name:'obj1},{name:'obj2'})
console.log(obj)

GC算法
Gc定义与作用:

  • GC就是垃圾回收机制的简写
  • GC可以找到内存当中的垃圾、并释放和回收空间

GC里的垃圾:

  • 程序中不再需要使用的对象
function func(){
   
	name = 'lg'
	return `${
     name} is a coder`
}
func()
  • 程序中不能再访问到的对象
function func(){
   
	const name = 'lg'
	return `${
     name} is a coder`
}
func()

GC算法的定义:

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

常见GC算法:

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

引用计数算法:

  • 核心思想:设置引用数,判断当前引用数是否为0
  • 引用计数器
  • 引用关系改变时修改引用数字
  • 引用数字为0时立即回收
    const a1 = {
         age:12}
    const a2 = {
         age:13}
    const a3 = {
         age:15}
    
    const nameList = [a1.age,a2.age,a3.age]
    
    function func(){
         
    	num1 = 1
    	num2 = 2
    }
    func()
    

引用计数算法优点:

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

引用计数算法缺点:

  • 无法回收循环引用的对象
  • 时间开销大
  • 资源消耗较大
function func(){
   
	const o1 = {
   }
	const o2 = {
   }
	o1.name = o2
	o2.name = o1
	return 'lg is a coder'
}
func()

标记清除算法:

  • 核心思想:分标记和清除两个阶段完成
  • 遍历所有对象找标记活动对象
  • 遍历所有对象清除没有标记对象
  • 回收相应的空间
    标记清除算法优点:可以回收循环引用的对象
    标记清除算法缺点:
  • 会产生空间碎片化问题,不能让空间最大化的使用
  • 不会立即回收垃圾对象

标记整理算法:

  • 标记整理可以看做是标记清除的增强
  • 标记阶段的操作和标记清除一致
  • 清除阶段会先执行整理,移动对象位置
    标记整理的优点:
  • 减少碎片化空间
    标记整理的缺点:
    +不会立即回收垃圾对象

3、V8引擎的垃圾回收

V8

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

V8垃圾回收策略

  • 采用分代回收的思想
  • 内存分为新生代、老生代
  • 针对不同对象采用不同算法
    在这里插入图片描述
    V8中常用GC算法
  • 分代回收
  • 空间复制
  • 标记清除
  • 标记整理
  • 标记增量

V8如何回收新生代对象
V8内存分配:在这里插入图片描述

  • V8内存空间一分为二,左边为新生代存储区,右边为老生代存储区
  • 小空间用于存储新生代对象(32M|16M)
  • 新生代指的是存活时间较短的对象(如局部变量)

新生代对象回收实现

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

回收细节说明:

  • 拷贝过程中可能出现晋升
  • 晋升就是将新生代对象移动至老生代
  • 一轮GC还存活的新生代需要晋升
  • To空间的使用率超过25%

V8如何回收老生代对象
老生代对象说明

  • 老生代对象存放在右侧老生代区域
  • 64位操作系统1.4G,32位操作系统700M
  • 老生代对象就是指存活时间较长的对象(全局变量、闭包)

老生代对象回收实现:

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

细节对比:

  • 新生代区域垃圾回收使用空间换时间
  • 老生代区域垃圾回收不适合复制算法

标记增量如何优化垃圾回收:在这里插入图片描述

4、Performance工具

为什么使用Performance

  • GC的目的是为了实现内存空间的良性循环
  • 良性循环的基石是合理使用
  • 时刻关注才能确定是否合理
  • Performance提供多种监控方式
  • 通过Performance时刻监控内存

Performance使用步骤

  • 打开浏览器输入目标网址
  • 进入开发人员工具面板,选择性能
  • 开始录制功能,访问具体界面
  • 执行用户行为,一段时间后停止录制
  • 分析界面中记录的内存信息

内存问题的体现

内存问题的外在表现:

  • 页面出现延迟加载或经常性暂停(频繁垃圾回收,内存爆表)
  • 页面持续性出现糟糕的性能(内存膨胀)
  • 页面的性能随时间延长越来越差(内存泄漏)

监控内存的几种方式

界定内存问题的标准:

  • 内存泄漏:内存使用持续升高
  • 内存膨胀:在多数设备上都存在性能问题
  • 频繁垃圾回收:通过内存变化图进行分析

监控内存的几种方式:

  • 浏览器任务管理器
  • Timeline时序图记录
  • 堆快照查找分离DOM
  • 判断是否存在频繁的垃圾回收

1、任务管理器监控内存:
启动浏览器任务管理器:Shift+Esc在这里插入图片描述
2、Timeline记录内存变化:在这里插入图片描述
3、堆快照查找分离DOM
什么是分离DOM:

  • 界面元素存活在DOM树上
  • 垃圾对象时的DOM节点
  • 分离状态的DOM节点
    在这里插入图片描述
    在这里插入图片描述

4、判断是否存在频繁的垃圾回收:

  • GC工作时应用程序是停止的
  • 频繁且过长的GC会导致应用假死
  • 用户使用中感知应用卡顿

确定频繁的垃圾回收:

  • Timeline中频繁的上升下降
  • 任务管理器中数据频繁的增加减小

5、代码优化

如何精准测试JavaScript性能

  • 本质上是采集大量的执行样本进行数学统计和分析
  • 使用基于Benchmark.js的https://jsperf.com/完成

Jsperf使用流程

  • 使用GitHub账号登录
  • 填写个人信息(非必须)
  • 填写详细的测试用例信息(title,slug)
  • 填写准备代码(DOM操作时经常使用)
  • 填写必要有setup与teardown代码
  • 填写测试代码片段

慎用全局变量

  • 全局变量定义在全局执行上下文,是所有作用域链的顶端
  • 全局执行上下文一直存在于上下文执行栈,直到程序退出
  • 如果某个局部作用域出现了同名变量则会遮蔽或污染全局
    //全局变量
    var i, str = ''
    for (i = 0; i < 1000; i++) {
         
        str += i
    }
    
    //局部变量
    for (let i = 0; i < 1000; i++) {
         
        let str = ''
        str += i
    }
    

缓存全局变量
将使用中无法避免的全局变量缓存到局部。

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>缓存全局变量</title>
</head>

<body>
    <input type="button" value="btn" id="btn1">
    <input type="button" value="btn" id="btn2">
    <input type="button" value="btn" id="btn3">
    <input type="button" value="btn" id="btn4">
    <p>1111</p>
    <input type="button" value="btn" id="btn5">
    <input type="button" value="btn" id="btn6">
    <p>2222
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值