四、使用Performance监控内存

Performance性能优化V8

# 内存管理为什么要监控?

GC的目的是为了实现内存空间的良性循环,良性循环的基石是对内存空间合理使用。因为HTML中没有提供内存操作的API,所以时刻关注内存变化才能确定是否合理,我们可以通过Performance这个工具定位问题所在,Performance工具提供多种监控方式,可以时刻监控内存。

# Performance工具使用流程

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

下面的截图,可以看到内存Memory的选项出来的折线图就表示了内存的时候情况,有升有降就是正常的工作:

# 内存问题

# 产生内存问题的现象及本质

限定当前网络问题正常的情况下:

出现的现象 存在的问题
页面出现延迟加载或者经常性暂停 内存有问题,与GC频繁的垃圾回收操作是有关联的,一定存在代码中有让内存爆掉的情况
页面持续性出现糟糕的性能 存在内存膨胀,当前界面为了达到最佳使用速度,会申请一定的内存空间,这个内存空间大小超过了上限
页面的性能随时间延长越来越差 伴随内存泄露

# 界定内存问题的标准

  • 内存泄露 :内存使用持续升高
  • 内存膨胀 :当前应用程序本身为了达到最优效果需要一定的内存空间,也许与当前设备本身硬件不支持有关,所以产生了性能上的差异,需要去判断是程序的问题还是设备的问题。需要多做测试,在多数设备上都存在性能问题说明程序本身有问题。
  • 频繁垃圾回收 :通过内存变化图进行分析

# 监控内存的几种方式

# 浏览器任务管理器

以数值的形式将程序在执行的过程中内存的变化提现出来

在浏览器中 -> shift+Esc -> 右键选中【JavaScript使用的内存】

  • 第一列的内存是原生内存【DOM节点占据的内存】,这个数据增大表示创建了新的DOM节点。
  • 最后一列JavaScript内存是堆内存,界面中所有可达对象正在使用的内存大小。这个数据增大表示要么在创建新对象,要么对象在不断的增长。

下面进行一下实例:

<!--创建一个长度很大的数组-->
<body>
  <button id="add">Add</button>
  <script>
    const add = document.getElementById('add')
    add.onclick = function() {
      let arrList = new Array(1000000)
    }
  </script>
</body>
1
2
3
4
5
6
7
8
9
10

点击按钮知道内存会增大,是因为我们创建了很大的数组。之后的脚本,可以拿这个去进行监控。

# Timeline时序图记录

上面的方法只能说我们的内存存在问题,但是无法更精确的定位什么时间发生的,和那些代码有关系。我们可以通过Timeline时序图直接把当前应用程序的走势以时间点的方式呈现出来。

下面进行一下实例:

<body>
  <button id="btn">Add</button>
  <script>
    const arrList = []
    function test() {
      for( let i = 0 ; i < 100000; i++) {
        document.body.appendChild(document.createElement('p'))
      }
      arrList.push(new Array(1000000).join('x'))
    }

    document.getElementById('btn').addEventListener('click',test)
  </script>
</body>
1
2
3
4
5
6
7
8
9
10
11
12
13
14

进入页面,打开performance进行录制,点击三次按钮,停止录制。

可以看到内存是正常的,有升有降,降的地方就是GC在工作了。如果没有降的地方,就是有问题的地方。

# 堆快照查找分离DOM(可以观察内存泄露)

堆快照是很有针对性的查找当前的界面对象中是否存在一些分离的DOM,分离DOM的存在也就是存在内存泄漏。
所以先搞清楚一下 什么是分离DOM?DOM存在的几种状态?

# DOM存在的几种状态?

  • 界面元素存活在DOM树上
  • 垃圾对象时的DOM节点 —— DOMDOM树上脱离了,js里面也没有引用。
  • 分离状态的DOM节点 —— DOMDOM树上脱离了,js里面有引用,界面上看不见,存在内存里面。

# 实例

下面实例看一下,下面创建了ulli,这里没有在页面中呈现,但是代码中有引用,这些就是分离DOM

<!---->
<body>
  <button id="btn">Add</button>
  <script>
    var tmpEle

    function fn() {
      var ul = document.createElement('ul')
      for (var i = 0; i < 10; i++) {
        var li = document.createElement('li')
        ul.appendChild(li)
      }
      tmpEle = ul
    }

    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
18

打开浏览器 -> Memory -> Profiles -> Heap snapshot -> Take snapshot

点击Add按钮 -> Take snapshot -> 可以看到多出来的ul标签和li标签,这个就是分离DOM

# 怎么处理分离DOM?

函数中把temEle置为null即可

var tmpEle
function fn() {
  var ul = document.createElement('ul')
  for (var i = 0; i < 10; i++) {
    var li = document.createElement('li')
    ul.appendChild(li)
  }
  tmpEle = ul
  tmpEle = null
}
1
2
3
4
5
6
7
8
9
10

这个时候时候堆快照,分离DOM就没有了。

# 判断是否存在频繁的垃圾回收(需要用不同的工具)

# 为什么要判断?

因为GC工作时应用程序是停止的,如果当前GC频繁工作,而且时间过长的话对Web应用来说很不友好,会导致应用假死说我状态,用户使用中会感知应用有卡顿。

# 具体怎么用监控判断?

  1. 通过 Timeline时序图 判断,对当前性能面板中的内存走势进行监控,如果其中频繁的上升下降,就出现了频繁的垃圾回收。这个时候要定位代码,看看是执行什么的时候造成了这种情况。
  2. 使用 浏览器任务管理器 会简单一些,任务管理器中主要是数值的变化,其数据频繁的瞬间增加减小,也是频繁的垃圾回收。
更新时间: 2021-09-15 12:03