性能优化是不可避免的。所有的程序,都需要在性能上进行优化,或运行速度,或运行空间。
任何一种提高运行效率,降低运行开销的行为,都可以看作是一种优化操作。
内容概要
- 内存管理
- 垃圾回收与常用GC算法
- V8引擎的垃圾回收
- Performance工具
内存管理
内存为什么需要管理
为了避免内存泄漏。
什么是内存管理
内存: 由可读写单元组成,表示一片可操作空间。
管理:人为的去操作一片空间的申请、使用、释放。
内存管理:开发者主动申请空间、使用空间、释放空间。
管理流程:申请-使用-释放
JS中的内存管理
JS中的内存管理是自动的。
申请内存空间:
1 | var obj = {} |
使用内存空间:
1 | obj.name = "this is name" |
释放内存空间:
1 | obj = null |
内存问题
- 内存泄露:内存使用持续升高
- 内存膨胀(内存溢出):在多数设备上都存在性能问题
- 频繁垃圾回收:通过内存变化图可以进行分析
内存出现问题的外在表现
- 页面出现延迟加载或经常性暂停 — 频繁的垃圾回收
- 页面持续性出现糟糕的性能 — 内存膨胀
- 页面的性能随着时间延长越来越差 — 内存泄漏
垃圾回收
jS中的垃圾
- 对象不再被引用时是垃圾
- 对象不能从根上访问到时是垃圾
可达对象
从根出发,可以访问到的对象即为可达对象。
在JS中,根可以理解为全局变量对象。
从根出发,通过引用或作用域链能访问到的对象都是可达对象。
垃圾回收算法(GC算法)
GC即垃圾回收机制的简写
GC工作的时候,会找到内存中的垃圾并释放和回收空间。
GC中的垃圾:
- 程序中不再需要使用的对象
- 程序中不能再访问到的对象
什么是GC算法:
- GC是一种机制,垃圾回收器完成具体的查找垃圾、释放空间、回收空间的工作。
- 算法是查找垃圾、释放空间、回收空间所遵循的规则。
引用计数
原理
内部通过一个引用计数器来维护当前对象的引用数。通过判断当前引用数值是否为0,来判断该对象是否为一个垃圾对象。若该对象的引用数为0,则GC开始工作,对其所在的对象空间进行回收和再使用。
当引用关系发生改变的时候,引用计数器就会修改引用数字。
当引用数字为0的时候,立即回收。
优缺点
优点
- 发现垃圾时回立即回收
- 减少程序卡顿时间
缺点
- 无法回收循环引用的对象
- 维护引用计数器需要额外的空间及时间开销
标记清除
原理
核心思想:将垃圾回收分为标记和清除两个阶段。
标记阶段,遍历所有对象,标记活动对象(可达对象)。
清除阶段,遍历所有对象,将没有标记的对象进行清除。同时,将第一个阶段的标记抹掉,方便下次进行标记清除工作。最后,回收相应空间。
优缺点
优点
- 可以回收循环引用的对象
缺点
- 会产生空间碎片化,浪费空间
- 不会立即回收垃圾对象
- 回收垃圾对象的时候,程序需要暂停执行
标记整理
标记清除的一个增强工作
原理
标记阶段: 与标记清除的标记阶段完全一致,遍历所有对象,标记所有活动对象(可达对象)
清除阶段:先执行整理,移动对象位置,是活动对象在地址上连续
然后执行清除工作,遍历所有对象,将没有标记的对象清除,回收相应的空间。
优缺点
优点
- 可以回收循环引用的对象
- 减少碎片化空间
缺点
- 不会立即回收垃圾对象
标记增量
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可以时刻监控内存
使用步骤
- 打开浏览器,输入目标地址
- 进入开发者工具面板,选择性能
- 开启录制功能,访问具体页面
- 执行用户行为,一段时间后停止录制
- 分析界面中记录的内存信息
使用堆快照功能查找分离DOM
什么是分离DOM
正常情况下,界面元素存活在DOM树上。
当元素从DOM树上进行了脱离,并且JS代码中也没有对该DOM元素的引用,则该DOM节点为一个垃圾对象。
当元素从DOM树上脱离了,但是JS代码中还有对该DOM元素的引用,则,该DOM节点为分离DOM。
分离DOM在界面上看不到,但是会占用内存空间,是一种内存泄漏。