JavaScript性能优化

JavaScript内存管理

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

JavaScript中的内存管理:
申请内存空间,使用内存空间,释放内存空间。

//申请空间
let obj = {}

//释放空间
obj.name = 'aa'

//使用空间
obj = null

JavaScript中的垃圾

JavaScript中内存管理是自动的(每当创建一个函数、数组等变量,会自动的为我们分配一定的空间)
对象不再被引用时是垃圾。
对象不能从根上访问到时是垃圾。

JavaScript中的可达对象:可以访问到的对象就是可达对象(引用、作用域链)。
可达的标准是从根出发是否能找到。
JavaScript中的根可以理解为全局变量对象(也就是全局执行上下文)。

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)

在这里插入图片描述
加入说obj1被delete了那么obj1是从根找不到的,obj1则成了垃圾,JavaScript引擎就会去回收它↓
在这里插入图片描述

GC算法

GC就是垃圾回收机制的缩写。
GC可以找到内存中的垃圾,并释放和回收空间。
GC里的垃圾是程序中不需要再使用的对象。
GC算法是什么:

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

常见GC算法:

  • 引用计数:通过一个数字,来判断当前的一个对象是不是一个垃圾
  • 标记清除:给活动对象添加一个标记,看其是不是一个垃圾
  • 标记整理
  • 分代回收

引用计数算法实现原理

核心:设置引用数,判断当前引用数是否为0
引用计数器
引用关系改变时,会去修改引用数字
引用数字为0时立即回收

const user1 = {age: 11}
const user2 = {age: 12}
const user3 = {age: 13}

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

function fn () {
    //没有变量声明,直接被挂载到了window对象
    // num1 = 1
    // num2 = 2

    //内部声明了变量,当fn执行之后,num1,num2的引用计数为0
    const num1 = 1
    const num2 = 2
}

fn()

优点:

  • 发现垃圾时立即回收
  • 最大程度减少程序暂停(内存快爆满的情况下立刻寻找引用数值为0的,然后去释放内存)

缺点:

  • 无法回收循环引用的对象
  • 时间开销大
function fn1() {
    const obj1 = {}
    const obj2 = {}
    //由于有互相指引的关系,所以引用计数不为0
    obj1.name = obj2
    obj2.name = obj1

    return true
}
fn1()

标记清除算法原理

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

会从全局变量开始向下遍历每一个子节点,a1,b1成为了局部变量,会被清除回收。回收的数据会被放到空闲列表上,后续添加的内容会直接从空闲列表中取。相比于引用计数而言,我们可以让循环引用的能够回收。
在这里插入图片描述
缺点:地址不连续。比如A对象有2个字节的空间,B对象有一个字节的空间,但是释放空间的时候并不会生成一个3个字节的空间,还是有一个2字节和一个1字节的两个空间,假如正好有个C对象新增进来,那么找2字节的空间就会多出0.5字节,找B又不够。

标记整理算法实现原理

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

V8

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

V8垃圾回收策略

采用分代回收的思想
内存分为新生代和老生代

V8中常用GC算法:

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

v8如何回收新生代对象

V8内存分配:
V8内存空间一分为二
小空间用于存储新生代对象(32M | 16M)
新生代指的是存活时间较短的对象(比如局部变量)

新生代对象回收实现:

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

回收细节说明:

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

V8如何回收老生代对象:

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

老生代对象回收实现:

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

细节对比:

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

增量标记如何优化垃圾回收:
让程序跟垃圾回收交替执行

Perfermance

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

界定内存问题的标准:
内存泄漏: 内存使用持续增高
内存膨胀:在多数设备中都存在性能问题
频繁垃圾回收:通过内存变化图进行分析

什么是分离DOM:

  • 界面元素存活在DOM树上
  • 垃圾对象时的DOM节点
  • 分离状态的DOM节点

慎用全局变量

  • 全局变量定义在全局执行上下文,是所有作用域链的顶端
  • 全局执行上下文一直存在于上下文执行栈,直到程序退出
  • 如果某个局部作用域出现了同名变量则会遮蔽或污染全局

缓存全局变量

将不可避免的全局变量写在局部作用域中,可以大大提升性能。

通过原型对象添加附加方法

在原型对象上新增实例对象需要的方法

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值