一、javascript中的垃圾回收
性能优化是不可避免的,任何一种可以提供运行效率降低运行开销的行为都可以称为性能优化。JavaScript中内存管理是自动的。对象不再被引用的时或对象不能从根上访问到的时候都被称之为垃圾。
内存管理介绍
- 内存:由可读写单元组成,表示一片可操作空间
- 管理:人为的去操作一片空间的申请、使用和释放
- 内存管理:开发者主动申请空间、使用空间、释放空间
- 管理流程:申请-使用-释放
javascript中的垃圾
- javascript中的内存管理是自动的
- 对象不再被引用时是垃圾
- 对象不能从根上访问时是垃圾
JavaScript中的可达对象
- 可以访问到的对象就是可达对象(引用、作用域链)
- 可达的标准就是从根出发是否能够被找到
- JavaScript中的根可以理解为全局变量对象
二、GC算法是什么
- GC是一种机制,垃圾回收器完成的具体的工作
- 工作的内容就是查找垃圾释放空间、回收空间
- 算法就是工作时查找和回收所遵循的规则
常见的GC算法
- 引用计数
- 标记清除
- 标记整理
- 分代回收
引用计数算法实现原理
- 核心思想:设置引用数,判断当前引用数是否为0
- 引用计数器
- 引用关系改变时修改引用数字
- 引用数字为0时立即回收
其思路是对每个值都记录它被引用的次数。声明变量并给它赋一个引用值时,这个值的引用数为1.如果同一个值又被赋给另一个变量,那么引用数加1.类似地,如果保存对该值引用地变量被其他值给覆盖了,那么引用数-1.当一个值地引用数为0时,就说明没办法再访问到这个值了,因此可以安全地回收其内存了。垃圾回收程序下次运行的时候就会释放引用数为0的值的内存。
let a = {a: 123}
{a:123}对象的引用计数值为1.
而当我们去除abc字符串对象的引用时,则abc字符串对象的引用计数减1
a = null
由此可见,当对象的引用计数为0时,垃圾回收就发生了。
引用计数算法很简单,它实际上是通过在对象头中分配一个空间来保存该对象被引用的次数。如果该对象被其它对象引用,则它的引用计数加一,
如果删除对该对象的引用,那么它的引用计数就减一,当该对象的引用计数为0时,那么该对象就会被回收。
const user1 = { age: 11 }
const user2 = { age: 12 }
const user3 = { age: 13 }
const nameList = [user1.age, user2.age, user3.age] // user1、user2、user3因为还在被引用不会被回收
function fn() {
const num1 = 1
const num2 = 2
}
fn() // num1和num2会被回收
三、引用计数算法优缺点
引用计数算法优点
- 发现垃圾时立即回收
- 最大限度减少程序的暂停
引用计数算法的缺点
- 无法回收循环引用对象
- 时间开销大
对象之间互有引用的情况,即使对象本身没有被使用,但是引用存在就导致了引用计数不为0,无法被回收的情况。
详:如果一个对象的引用数为0,则该对象将被回收。程序运行时,对内存的消耗除逻辑代码外,也包括了GC算法的消耗,引用计数固然可以在程序出现垃圾的时候可以及时回收释放内存,也因内存可以不断得到释放而减少了程序暂停的时间,但是GC同时也需要维护‘roots’表来统计引用计数,当代码中引用较多时,也会带来损耗。同时对于 对象之间互有引用的情况,即使对象本身没有被使用,但是引用存在就导致了引用计数不为0,无法被回收的情况。
funcion fn() {
const obj1 = {}
const obj2 = {}
obj1.name = obj2
obj2.name = obj1
return 'lg is a coder'
}
fn() // obj1和obj2之间存在相互引用没有被回收
四、标记清除算法实现原理
标记清除算法会递归的寻找对象之间的引用获取所有可达对象,并为其做上标记, 但是标记清除算法所回收的内存地址在内存上不一定是连续的,这就导致了内存空间的碎片化(类似磁盘碎片), 浪费空间,并且标记清除法是不会立即回收对象的,而且当标记清除法运行当时候,程序会被暂停。
- 核心思想:分为标记和清除两个阶段完成
- 遍历所有对象找标记活动对象
- 遍历所有对象清除没有标记对象
- 回收相应的空间
五、 标记清除算法优缺点
优:相对于引用计数可以解决循环引用不能回收的问题
缺:会产生空间碎片化的问题,不能让我们的空间得到最大使用
六、标记整理算法原理
- 标记整理可以看做是标记清除的增 强
- 标记阶段的操作和标记清除一样
- 清除之前会进行整理,移动对象的位置
常见GC算法总结:
引用计数优缺点
- 可以即时回收垃圾对象
- 减少程序卡顿时间
- 无法回收循环引用的对象
- 资源消耗较大
标记清楚优缺点
- 可以回收循环引用的对象
- 容易产生碎片化空间,浪费空间
- 不会立即回收垃圾对象
标记整理优缺点
- 减少碎片化空间
- 不会立即回收垃圾对象
七、认识V8
- v8是一款主流的JavaScript执行引擎
- v8采用即时编译
- v8内存设限 (64位系统1.5G,32位系统800M)
V8垃圾回收策略
- 采用分代回收思想
- 内存分为新生代、老生代
- 针对不同对象采用不同算法
V8引擎中的GC算法
- 分代回收
- 空间复制
- 标记清除
- 标记整理
- 标记增量
V8内存分配
- V8内存空间一分为二
- 小空间用于存储新生代对象(32M | 16M)
- 新生代指的是存活时间较短的对象
九、新生代对象回收实现
- 回收过程采用复制算法+标记整理
- 新生代内存区分为二个等大小空间
- 使用空间为From,空闲空间为To
- 活动对象存储于From空间
- 标记整理后将活动对象拷贝至To
- From于To交换空间完成释放
回收细节说明
- 拷贝过程中可能出现晋升
- 晋升就是将新生对象移动至老生代
- 一轮GC还存活的新生代需要晋升
- To空间的使用率超过25%
十、老年代对象说明
- 老年代对象存放在右侧老生代区域
- 64位操作系统1.4G,32位操作系统700M
- 老年代对象就只指存活时间较长的对象
老年代对象回收实现
- 主要采用标记清除、标记整理、增量标记算法
- 首先使用标记清除完成垃圾空间的回收
- 采用标记整理进行空间优化
- 采用增量标记进行效率优化
细节对比
- 新生代区域垃圾回收使用空间换时间
- 老生代区域垃圾回收不适合复制算法
十一、V8总结
- V8是一款主流的javascript执行引擎
- V8内存设置上限
- V8采用基于分代回收思想实现垃圾回收
- V8内存分为新生代和老生代
- V8垃圾回收常见的GC算法
十二、performance工具介绍
- GC的目的是为了实现内存空间的良性循环
- 良性循环的基石是合理使用
- 时刻关注才能确定是否合理
- performance提供多种监控方式
performance使用步骤
- 打开浏览器输入目标
- 进入开发人员工具面板,选择性能
- 开启录制功能,访问具体界面
- 执行用户行为,一段时间后停止录制
- 分析界面中记录的内存信息
内存问题的外在表现 - 页面出现延迟加载或经常性暂停
- 页面持续性出现糟糕的性能
- 页面的性能随时间的延长越来越差
内存问题界定标准
- 内存泄漏:内存使用持续升高
- 内存膨胀:在多数设备上都存在性能问题
- 频繁垃圾回收:通过内存变化图进行分析
监控内存的几种方式
1.浏览器任务管理器
2.Timeline时序图记录
3.堆快照查找分离DOM
- 浏览器任务管理器
快捷键Shift+Esc调出任务管理器
点击按钮
-
TimeLine时序图记录
-
堆快照查找分离DOM
什么是分离DOM
(1)界面元素存活在DOM树上
(2)垃圾对象时的DOM节点
(3)分离状态时的DOM节点
- 判断是否存在频繁的垃圾回收
为什么确定频繁的垃圾回收
- GC工作时应用程序是停止的
- 频繁且过长的GC会导致应用假死
- 用户使用中感知应用卡顿
确定频繁的垃圾回收
- TimeLine中频繁的上升下降
- 任务管理器中数据频繁的增加减少