前言
在了解过浏览器内存模型以及事件循环之后,这仅仅是从一个代码的运行上了解了我们前端工程师编写的code是怎么样存储的以及怎么样去运行,了解了这些之后,我们就需要了解怎么样去使用合适的工具追踪我们内存中的变量以及运行中的代码,以加深我们对整个前端环境的理解、提高前端开发以及查找内存漏洞的效率,同时可以更加迅速的对前端页面加载等优化作出更好的决策。
本系列文章主要对一下工具进行介绍:
- Chrome任务管理器
- Chrome Performance
- Chrome Memory
Chrome任务管理器
点击右上角工具菜单->更多工具->任务管理器
任务管理器内存
关于内存比较重要两个选项是内存空间占用以及JavaScript使用的内存这两项
- 内存空间占用使用的是本机内存,DOM节点相关内存都存储在本机内存中,该内存浏览器并非进程独享,比如说GPU进程要加速某个标签进程中的DOM渲染,就需要获取该DOM树内存。
- JavaScript 使用的内存则是当前标签进程JavaScript独享的内存,用于存储JavaScript运行中的相关对象,这一列涉及到两个值,一个是对象值,一个是实际对象值
- 对象值比较好理解,就是当前JavaScript运行当中,所有对象占用内存的总和
- 实际对象值,底层上来讲内存中依旧存在对该对象的引用,没有被GC标记为清楚额对象都为实际对象
Chrome Performace
Performace工具面板通过时间轴,JavaScript内存占用使用情况,节点数,堆占用,事件循环日志等多方面的显示了当前标签的时间段内的运行状况,也不仅仅具现于内存分析。
打开控制台上的Performace面板 ,勾选Memory选项,开始录制前先点击垃圾回收-->点击开始录制-->点击垃圾回收-->点击结束录制
以图中已经录制了一定时间段内的页面的在改时间线上的性能变化,图中产生的内容由上至下分别是:
- 整个性能分析录制过程的Overview,勾选快照后,Overview中会将录制过程中的内存变化以及快照按时间线绘制出来
- 第二栏是整个性能分析的详细的,性能堆叠图,详细的标识出了录制过程中的,网络请求,主线程事件循环过程等,其中内存分析中我们更关注主线程事件循环(Main),其中会标识出所有事件方法的调用栈,录制结果的调用内存,横向表示时会出现带有更多详情的浮窗间,垂直方向表示调用栈,从上往下表示函数调用。滑动鼠标滚轮可以查看某段时间的调用栈信息。把鼠标放到调用栈的某个项目上面可以查看函数详细信息,调用的时间以及会对较长的标识红色角标,可以详细查看该调用栈,查看该项目是否需要优化。
- 第三栏你可以看到内存使用情况,分别显示以下内容:JS heap(JS堆),documents(文档),DOM nodes(DOM节点),listeners(侦听器)和GPU memory(GPU内存)。勾选或取消勾选复选框可以将其从图表中显示或隐藏。
- 第四栏则是在第二栏或者第三栏去确定点击某个调用点以及时间点时,给出相应的该项目的分析:包括Summary中的聚合时间分析以及总体时间查看,聚合时间中将以饼图的形式分析该项目整个调用栈的调用时间占比;Bottom-up则是给出了该项目调用栈自底向上的调用列表;Call-tree则是给出调用树的打印;Event log标识出了整个任务中事件循环中日志信息。
以上图窗我们要关注第二栏标识出的长任务是否需要优化,第三栏中的内存、节点数量、监听器数量的变化,以及点击之后,第四栏中的调用栈具体数据来查看性能变化以及异常等。
Chrome Memory
打开控制台上的Memory面板,会发现相应的录制按钮等操作。
开始录制前先点击下垃圾回收-->点击开始录制。
如果JS堆内存动态分配时间线,结束之前要再点击下垃圾回收,再结束录制
内存分析类型
内存快照
展示了整个页面的JavaScript对象以及DOM节点的内存分配
Summary 总览视图
按构造函数分组。用于捕捉对象及其使用的内存。对于定位DOM内存泄露特别有用。
- Contructor - 表示使用此构造函数创建的所有对象
- Distance - 显示使用节点最短简单路径时距根节点的距离
- Shallow Size - 显示通过特定构造函数创建的所有对象浅层大小的总和。浅层大小是指对象自身占用的内存大小(一般来说,数组和字符串的浅层大小比较大)
- Retained Size - 显示同一组对象中最大的保留大小。对象的最大保留内存,保留内存是指对象被删除后可以释放的那部分内存。
常见的顶层构造函数:
- (global property):全局对象和普通对象的中间对象,和常规思路不同。比如在Window上定义了一个Person对象,那么他们之间的关系就是[global] => (global property) => Person。之所以使用中间对象,是出于性能的考虑。
- (closure):使用函数闭包的对象。
- (array, string, number, regexp):一系列对象类型,其属性指向Array/String/Number/Regexp。
- HTMLDivElement/HTMLAnchorElement/DocumentFragment:元素的引用或者代码引用的指定文档对象。
快照可以用来发现DOM泄露。在Class filter(类过滤器)文本框中输入Detached可以搜索分离的DOM树。如果分离节点被JS引用,有可能就是泄露点。
如下代码,class filter中查找string,在string列表中找到'Hello World!'可以查看到testArray的内存引用。
var testArray = [ {value: 'Hello World!'} ];
Comparison 对比视图
对比两个快照。用于对比不同操作之后的堆快照,查看内存的释放及引用计数,来分析内存是否泄露及其原因。
- #New - Comparison 特有 - 新增项
- #Deleted - Comparison 特有 - 删除项
- #Delta - Comparison 特有 - 增量
- Alloc. Size - Comparison 特有 - 内存分配大小
- Freed Size - Comparison 特有 - 释放大小
- Size Delta - Comparison 特有 - 内存增量
var testArray = [ {value: 'Hello World!'} ]; function doSomething() { testArray.push({ value: 'Hello Everyone!' }) } document.querySelector('#btn').addEventListener('click', doSomething)
点击按钮后,数组中 push 了新的一项对象
Containment 内容视图
查看内存内容。更适合查看对象结构,有助于分析对象的引用情况。适用于分析闭包以及深入分析对象。
Statistics 统计视图
总览内存的统计信息
分配时间线
可以持续的记录堆分配的情况,随着时间变化记录内存信息
var testArray = [ {value: 'Hello World!'} ]; var count = 1; function doSomething() { count *= 5 testArray.push({ value: 'Hello Person_'+count }) } document.querySelector('#btn').addEventListener('click', doSomething)
每条线的高度与最近分配的对象大小对应,竖线的颜色表示这些对象是否仍然显示在最终的堆快照中。蓝色竖线表示在时间线最后对象仍然显示,灰色竖线表示对象已在时间线期间分配,但曾对其进行过垃圾回收。
录制过程中点击了五次按钮,出现了5次蓝色竖线,可以看出,时间线上内存的变化,对应的可以查看到数据能的数组值的变化。
分配抽样
内存信息采样,使用采样的方法记录内存分配。此配置文件类型具有最小的性能开销,可用于长时间运行的操作。它提供了由 javascript 执行堆栈细分的良好近似值分配
Chart
整个内存占用堆栈图信息,root是整个标签加载所需内存,向下逐步拆解形成的内存堆栈。
Heavy
Tree
Heavy模式是将堆栈图自底向上的罗列出来,Tree模式则是自顶向下的罗列出来