性能优化是不可避免的。所有的程序,都需要在性能上进行优化,或运行速度,或运行空间。

任何一种提高运行效率,降低运行开销的行为,都可以看作是一种优化操作。

内容概要

  1. 内存管理
  2. 垃圾回收与常用GC算法
  3. V8引擎的垃圾回收
  4. Performance工具

内存管理

内存为什么需要管理

为了避免内存泄漏。

什么是内存管理

内存: 由可读写单元组成,表示一片可操作空间。

管理:人为的去操作一片空间的申请、使用、释放。

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

管理流程:申请-使用-释放

JS中的内存管理

JS中的内存管理是自动的。

申请内存空间:

1
var obj = {}

使用内存空间:

1
obj.name = "this is name"

释放内存空间:

1
obj = null

内存问题

  1. 内存泄露:内存使用持续升高
  2. 内存膨胀(内存溢出):在多数设备上都存在性能问题
  3. 频繁垃圾回收:通过内存变化图可以进行分析

内存出现问题的外在表现

  1. 页面出现延迟加载或经常性暂停 — 频繁的垃圾回收
  2. 页面持续性出现糟糕的性能 — 内存膨胀
  3. 页面的性能随着时间延长越来越差 — 内存泄漏

垃圾回收

jS中的垃圾

  1. 对象不再被引用时是垃圾
  2. 对象不能从根上访问到时是垃圾

可达对象

从根出发,可以访问到的对象即为可达对象。

在JS中,根可以理解为全局变量对象。

从根出发,通过引用或作用域链能访问到的对象都是可达对象。

垃圾回收算法(GC算法)

GC即垃圾回收机制的简写

GC工作的时候,会找到内存中的垃圾并释放和回收空间。

GC中的垃圾

  1. 程序中不再需要使用的对象
  2. 程序中不能再访问到的对象

什么是GC算法

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

引用计数

原理

内部通过一个引用计数器来维护当前对象的引用数。通过判断当前引用数值是否为0,来判断该对象是否为一个垃圾对象。若该对象的引用数为0,则GC开始工作,对其所在的对象空间进行回收和再使用。

当引用关系发生改变的时候,引用计数器就会修改引用数字。

当引用数字为0的时候,立即回收。

优缺点

优点

  1. 发现垃圾时回立即回收
  2. 减少程序卡顿时间

缺点

  1. 无法回收循环引用的对象
  2. 维护引用计数器需要额外的空间及时间开销

标记清除

原理

核心思想:将垃圾回收分为标记和清除两个阶段。

​ 标记阶段,遍历所有对象,标记活动对象(可达对象)。

​ 清除阶段,遍历所有对象,将没有标记的对象进行清除。同时,将第一个阶段的标记抹掉,方便下次进行标记清除工作。最后,回收相应空间。

优缺点

优点

  1. 可以回收循环引用的对象

缺点

  1. 会产生空间碎片化,浪费空间
  2. 不会立即回收垃圾对象
  3. 回收垃圾对象的时候,程序需要暂停执行

标记整理

标记清除的一个增强工作

原理

标记阶段: 与标记清除的标记阶段完全一致,遍历所有对象,标记所有活动对象(可达对象)

清除阶段:先执行整理,移动对象位置,是活动对象在地址上连续

​ 然后执行清除工作,遍历所有对象,将没有标记的对象清除,回收相应的空间。

优缺点

优点

  1. 可以回收循环引用的对象
  2. 减少碎片化空间

缺点

  1. 不会立即回收垃圾对象

标记增量

V8老生代存储空间使用标记增量算法提升效率

原理

在标记阶段,采用分阶段标记,将程序运行与垃圾回收交替进行。不会长时间阻断程序运行。

优缺点

优点

​ 不会长时间阻断程序运行,提升程序运行效率,有利于用户体验。

缺点

​ 不会立即回收垃圾对象

分代回收

V8引擎采用了分代回收算法

原理

将内存空间分为新生代对象存储空间和老生代对象存储空间。

针对不同的存储空间,使用不同的垃圾回收机制。

空间复制

V8新生代存储空间采用了空间复制的垃圾回收算法。

原理

将存储空间分为两个区域,一个区域用于存放对象,称为From空间,一个区域空闲,称为To空间。

将From空间的活动对象复制到To空间,将From空间完全释放回收。再将From空间与To空间进行交换。

优缺点

回收速度快,是一种用空间换时间的算法

V8引擎

一款主流的JavaScript执行引擎

V8采用即时编译,可以将源码直接翻译成可以直接执行的机器码,所以其执行速度很快。

V8内存设有上限,在64位的操作系统上,上限为1.5G,在32位操作系统上,上限为800M。

为什么对内存设有上限?

    1. V8本身是为浏览器制造的,现有内存大小对于网页应用来数是足够的。
      2. V8内部采用的垃圾回收机制也决定了V8采用的这个上限设置是合理的。

V8垃圾回收策略

采用分代回收的思路。

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

V8将内存空间分为两部分,

其中较小的空间用于存储新生代对象,其空间大小有限制:在64位操作系统中,其大小为32M,在32位操作系统中,其大小为16M。

其中较大的空间用于存储老生代对象,其空间大小也有限制:在64为操作系统中,其大小为1.4G,在32为操作系统中,其大小为700M。

新生代对象:存活时间较短的对象。

老生代对象:存活时间较长的对象。

V8 新生代对象回收

回收过程采用 复制算法 + 标记整理算法

新生代内存区分为两个等大小的空间,其中一个空间用于存放活动对象,称其为From空间,另一个空间空闲,称其为To空间。

对Form空间进行标记整理后,将活动对象拷贝至To空间,将From空间进行完全释放。将From空间与To空间进行交换。

拷贝过程中可能会出现晋升:将新生代对象移至老生代空间

​ 会发生晋升的对象为:1. 记过一轮GC之后还存活的对象, 2. To空间的使用率超过了25%时,To空间中的活动对象

V8老生代对象回收

回收过程采用 标记清除算法 + 标记整理算法 + 标记增量算法

首先,使用标记清除算法完成垃圾空间的回收。

当新生代对象晋升到老生代空间,而老生代空间存储区不足以存放晋升对象时,采用标记整理进行空间优化。

采用标记增量算法进行效率优化。

标记增量算法

将标记操作拆分成多个小步,组合着完成这个回收。

将垃圾回收与程序执行交替工作,优化程序运行,提升用户体验。

Performance工具

通过Proformance可以时刻监控内存

使用步骤

  1. 打开浏览器,输入目标地址
  2. 进入开发者工具面板,选择性能
  3. 开启录制功能,访问具体页面
  4. 执行用户行为,一段时间后停止录制
  5. 分析界面中记录的内存信息

使用堆快照功能查找分离DOM

什么是分离DOM

正常情况下,界面元素存活在DOM树上。

当元素从DOM树上进行了脱离,并且JS代码中也没有对该DOM元素的引用,则该DOM节点为一个垃圾对象。

当元素从DOM树上脱离了,但是JS代码中还有对该DOM元素的引用,则,该DOM节点为分离DOM。

分离DOM在界面上看不到,但是会占用内存空间,是一种内存泄漏。