JS性能优化 —— Performance

6/11/2021 JS性能

# 为什么使用Performance

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

# Performance使用步骤

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

# 内存问题的体现

# 内存问题的外在表现

(假设网络环境正常)

  • 页面出现延迟加载或经常性暂停(可能由于有频繁的垃圾回收,说明代码让内存短时间沾满)
  • 页面持续性出现糟糕的性能(可能底层存在内存膨胀,当前界面为了达到一定的运行速度,而申请了更多的内存空间,从而超过了本机的承载)
  • 页面的性能随时间延长越来越差(可能存在内存泄漏,也就是代码执行过程中,回收的空间越来越少,越来越多的空间无法回收)

# 常见的内存监控方式

# 界定内存问题的标准

  • 内存泄漏:内存使用持续升高
  • 内存膨胀:在多数设备上都存在性能问题(可能在多种设备上体现,可能是由于硬件不支持,但如果对多个通用设备上测出相同的问题,就可能是程序本身的问题)
  • 频繁垃圾回收:通过内存变化图进行分析

# 监控内存方式

  • 浏览器任务管理器
  • Timeline时序图记录
  • 对快照查找分离DOM(分离DOM也就是一中内存泄漏)
  • 判断是否存在频繁的垃圾回收

# 任务管理器监控内存

  1. 在浏览器中通过右上角更多操作的更多工具找到任务管理器(或者使用快捷键Shift+Esc)
  2. 选中其中一栏右键选择JS内存,主要关注内存和JS内存
    • 内存表示原生内存,也就是DOM节点展示所需的内存,如果不断增大说明不停在操作DOM
    • JS内存表示JS堆,括号内表示可达引用使用的内存,如果没有变化说明没有增长,如果不停增长,说明GC没有运行,JS代码可能有问题
  3. 缺点是任务管理器监控内存只能告诉我们有没有问题,而不能定位问题

# Timeline记录内存

  • 在浏览器的任务管理器中无法定位具体问题,可以通过浏览器性能(performance)功能的时间线记录内存,可以让我们精确定位内存异常问题发生的时间和代码
  • 通过模拟一连串dom操作,会发现在dom操作期间内存瞬间飙升,然后趋于平稳,接着出现下降,然后再次点击时发现如此往复。
  • 内存占用的下降就说明v8的垃圾回收启动,在js代码趋于稳定期间运行垃圾回收清理内存。
  • 如果出现内存一直增长,没有内存的下降,就说明可能有内存问题,很有可能是内存泄漏
  • 我们还可以通过时间线监控在那个时段发生了内存问题,且可以看到此时的DOM变化。

内存变化图 图1 内存变化图

# 堆快照查找分离DOM

# 什么事分离DOM

  • 界面元素存活在DOM树上
  • 垃圾对象时的DOM节点(脱离了DOM树,且没有js代码引用的DOM节点)
  • 分离状态的DOM节点(脱了DOM树,在js代码中还有引用,虽然看不见,但在js内存中还占用空间)

演示分离DOM

<body>
    <button id="btn">add</button>
    <script>
        let tmpEle;

        function fn() {
            let ul = document.createElement("ul");

            for (let i = 0; i < 10; i++) {
                let li = document.createElement("li");
                ul.appendChild(li);
            }
            tmpEle = ul; // 此处只是将ul挂到了tmpEle上面, 但是又没有挂载到页面上, 这里的11个节点就是分离DOM
        }
        document.getElementById("btn").addEventListener("click", fn);
    </script>
</body>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

运行这个html然后在点击事件前后获取堆快照发现如图2和图3所示

点击前的堆快照 图2 点击前的堆快照图示

点击后的堆快照 图3 点击后的堆快照图示

  • 当未点击add之前,堆快照中未发现分离的DOM,而点击后则发现多出的11新的DOM节点,又没有挂在DOM树上都是分离的DOM
  • 要清除这些分离的DOM只需要,tmpEle = null,将temEle置为null即可,这样新产生的DOM节点就没有被引用,也就会被当做垃圾回收
  • 堆快照可用于专门查找分离DOM,这种页面中未渲染,却被对象引用,占用内存,这样就会造成内存浪费,我们就可以通过堆快照去查找这些分离的DOM节点然后在代码中将其清除,GC就会将其回收,避免内存浪费。

# 判断是否存在频繁GC

# 为什么确定频繁垃圾回收

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

# 确定频繁的垃圾回收

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

# Performance总结

  • Performance使用流程
  • 内存问题的相关分析(内存泄漏、内存膨胀、频繁GC)
  • Performance时序图监控内存变化
  • 任务管理器监控内存变化
  • 堆快照查找分离DOM
Last Updated: 1/21/2025, 10:16:53 AM