Chrome DevTools 之 Profiles,深度性能优化必备

虽然一直在用Chrome DevTools,但大多停留在常用的功能和调试上,比如Elements/Network/Sources/Console等主要功能,而对于性能分析/优化相关的Timeline/Profiles一直敬而远之,深感其门槛高,于是潜心阅读文档,以望窥得一二,以解决实际问题。

不同版本的Chrome DevTools差别很大,这篇文章基于最新版的Mac Chrome完成,主要介绍Network/Timeline/Profiles三个方面的内容。

系统版本 & Chrome版本

之前写过一篇关于渲染性能的长文章,有兴趣的可以先阅读阅读。

Profiles 有哪些功能?

Profiles用来更深入的分析性能问题、内存问题。之前介绍的Timeline可以直观的发现问题所在,但是要深入分析,定位问题原因,还得靠Profiles。


Profiles 功能

如截图所示,Profiles提供了三个主要功能:
Collect Javascript CPU Profile:用于找出耗时较长的JS函数。
Take Heap Snapshot:用于分析JS对象及其关联的DOM节点的内存分布。
Record Heap Allocations:记录内存分配信息,用于分析内存泄露的情况。

Profiles 下 Collect Javascript CPU Profile,可以做什么?

如果你在Timeline中发现JS代码存在jank,jank在渲染性能提到过,主要指运行时间过长而丢帧的问题。使用Profiles可以找出耗时较长的JS函数,Profiles的使用步骤如下:
1、勾选Profiles > Collect Javascript CPU Profile
2、点击Start,开始录制。
3、结合具体要分析的场景,做一些操作,比如:刷新页面,点击某个按钮,滑动某个图片,甚至不操作。
4、点击Stop,结束录制。

选中函数,点击X符号,可以移除该函数,通过这种方式可以避免一些无用函数或者插件代码对结果分析造成干扰。也可以点击刷新符号,恢复移除的函数。

Paste_Image.png

查看录制结果

录制结束之后,我们来查看录制的结果,Chrome DevTools提供了三种不同的查看方式:
Chart 图表:按时间顺序展示的火焰图。
Heavy(Bottom Up) 从下至上:按对性能的影响列出函数,也可以查看函数的调用路径,默认视图。
Tree(Top Down) 从上至下:从调用栈的顶端开始,展示调用结构的树状图。

Chart 图表视图

Chart:按时间顺序展示的火焰图。包含两部分:Overview 总览Call Stacks调用栈

Paste_Image.png

Overview 总览:录制结果的概览,该图中的条形高度表示调用堆栈的深度。
Call Stacks调用栈:录制结果的调用堆栈,横向表示时间,垂直方向表示调用栈,从上往下表示函数调用。
通常来说,垂直方向并没有太大的意义,仅仅表示函数嵌套比较深而已,但是横向表示调用时间,如果调用时间太长,那么就需要优化优化了。

把鼠标放到调用Call Stacks调用栈的某个函数上面,会出现带有更多详情的浮窗:

函数详情

Name:函数名称。
Self time:本次该函数调用运行的时间,不包含子函数的时间。
Total time:本次该函数调用运行的总时间,包括子函数的时间。
URL:函数定义在文件中的位置,比如file.js:100,表示函数在file.js文件的第100行定义。
Aggregated self time:汇总所有该函数调用运行的时间,不包含子函数的时间。
Aggregated total time:汇总所有该函数调用运行的时间,包含子函数的时间。
Not optimized:优化器检查到该函数有可优化的空间。

Heavy(Bottom Up)视窗

按对性能的影响列出函数,也可以查看函数的调用路径,默认视图。


Heavy查看方式
Tree(Top Down)视窗

从最初调用的位置开始,展示调用结构的树状图。


Tree查看方式

Profiles 下 Take Heap Snapshot,可以做什么?

通过Profiles > Take Heap Snapshot可以查看堆快照,进而发现内存泄露问题。

Chorme DevTools的堆分析器可展示出JS对象及其关联的DOM节点的内存分布情况,可以利用该工具来录制堆快照、分析内存图、对比堆快照、定位内存泄漏。

录制快照

选中Take Heap Snapshot,点击开始即可获取快照(它会自动停止),每次获取快照之前都会自动执行垃圾回收操作。
快照最初是存储在渲染进程的内存中的,当我们录制的时候才会传输到Chrome DevTools,快照加载进来并解析,我们可以看到其具体信息,每个快照底部的数字就是可达的JS对象的大小。

录制快照

点击清除按钮可以清除快照,如果直接关闭Chrome DevTools窗口是不会清除快照的,重新打开Chrome DevTools窗口,之前的快照都还保留着。

不同类型的任务可以使用不同的方式来查看,堆快照有三种查看方式。

Paste_Image.png

Summary 总览视图:按构造函数分组。用于捕捉对象及其使用的内存。对于定位DOM内存泄露特别有用。
Comparison 对比视图:对比两个快照。用于对比不同操作之后的堆快照,查看内存的释放及引用计数,来分析内存是否泄露及其原因。
Containment 内容视图:查看堆内容。更适合查看对象结构,有助于分析对象的引用情况。适用于分析闭包以及深入分析对象。
Statistics 统计视图:总览堆的统计信息。

查看快照Sumary view 总览视图

默认视图,按对象统计,点击某个对象,展开查看更多的实例信息。

Paste_Image.png

Constructor:构造函数,节点下的对象都是由改构造函数创建而来。
Distance:与根节点的距离。
Objects Count:对象个数及百分占比。
Shallow size:对象的直接内存总数,直接内存是指对象自身占用的内存大小。
Retained size:对象的最大保留内存,保留内存是指对象被删除后可以释放的那部分内存。

点击展开构造函数,可以看到所有构造函数相关的对象实例,@后面的数字是该对象实例的唯一标识符。
记住,黄色的对象实例表示它被JS代码引用,红色的对象实例表示被黄色节点引用的游离节点。

那么,常见的顶层构造函数分别代表什么呢?
(global property):全局对象和普通对象的中间对象,和常规思路不同。比如在Window上定义了一个Person对象,那么他们之间的关系就是[global] => (global property) => Person。之所以使用中间对象,是出于性能的考虑。
(closure):使用函数闭包的对象。
(array, string, number, regexp):一系列对象类型,其属性指向Array/String/Number/Regexp
HTMLDivElement/HTMLAnchorElement/DocumentFragment:元素的引用或者代码引用的指定文档对象。

Paste_Image.png
查看快照Comparison view 对比视图

对比两个快照的不同,找出内存泄露的对象。


Comparison view 对照视图

为了验证特定操作会不会引起内存泄露,对比快照的步骤如下:
1、无任何操作,拍第一个堆快照
2、执行一个操作,通常是你觉得可能造成内存泄露的操作
3、执行相反的操作,比如打开文档,然后关闭它
4、拍第二个堆快照,切换到对照视图,并且指定与第一个堆快照进行对比

查看快照Containment view 内容视图

内容视图其实就是应用对象结构的鸟瞰图。它能让你深入分析函数闭包,观察VM内部对象,查看应用底层的内存使用情况。

Paste_Image.png

该视图提供了几个常见入口:
DOMWindow objects:JavaScript代码的全局对象
Native objects:浏览器的原生对象,整合到JS虚拟机中便于操作,比如DOM节点,CSS规则。

关于闭包的一个小建议:命名函数的闭包相对匿名函数的闭包更易于分析调试。

// 匿名闭包
function createLargeClosure() {
    var largeStr = new Array(1000000).join('x');

    var lC = function() {
        return largeStr;
    };

    return lC;
}


// 命名闭包
function createLargeClosure() {
    var largeStr = new Array(1000000).join('x');

    var lC = function lC() {
        return largeStr;
    };

    return lC;
}
Paste_Image.png
查看快照Statistics 统计视图

总览堆栈的统计信息。可以清楚的看出堆快照的分布情况。


Statistics 统计
搜索指定对象

当然,chrome提供了command + F,可以让你搜索指定的对象。

举个内存泄露的栗子
var select = document.querySelector;
var treeRef = select("#tree");
var leafRef = select("#leaf");
var body = select("body");

body.removeChild(treeRef);
//由于treeRef,#tree不能被释放

treeRef = null;
//由于leafRef,#tree仍然不能被释放

leafRef = null;
//完全没有被引用,#tree终于可以被释放
Paste_Image.png

#leaf节点递归引用了#tree节点,所以,只有当leafRef被设置成null后,#tree代表的整个树节点才会被垃圾回收器回收。

Profiles 下 Record Heap Allocations ,可以做什么?

通过Profiles > Record Heap Allocations可以持续的记录堆分配的情况,显示了对象在什么时候被创建、什么时候存在内存泄漏等。

Paste_Image.png

堆分配分析器

要想了解对象的堆分配情况,和其他工具类似:选中Record Heap Allocations=>点击Start开始录制=>做一系列操作=>停止录制=>分析。

堆分配
Paste_Image.png

上面的柱条表示堆中生成的新对象。高度表示这个对象的大小,颜色表示这个对象的内存释放情况:蓝色柱表示这个对象在timeline中生成,结束前仍然存在;灰色柱表示这个对象在timeline中生成,但结束前已经被回收了。
我们可以重复执行某个动作,如果最后有不少蓝色柱被保留,这些蓝色柱就是潜在的内存泄露问题。
如果左边的意料之外的蓝条,那么极有可能存在内存泄露。

上面总共录制了6次堆分配行为,我们可以聚焦到某一次堆分配,查看具体对象的保留路径信息。


Paste_Image.png

通过检查对象的保留路径可以弄清楚为什么对象没有被回收,从而移除一些没必要的引用,释放内存。

又要总结了?

该系列主要介绍了Chrome DevTools中的几个硬骨头Network/Timeline/Profiles,详细的介绍了他们的功能及一些使用技巧。希望大家读了这个系列后会对Chrome DevTools有一个大致的了解,并且能够亲身使用。当然,网站性能优化的话题还没有结束,后面还要结合实战讲讲:
怎么去发现jank并解决?
怎么发现强制同步布局并解决?
怎么发现内存泄露并解决?
怎么分析网页加载并优化加载速度?
等等...

系列文章
网站性能优化 之 Chrome DevTools(一)
网站性能优化 之 Chrome DevTools(二)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,561评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,218评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,162评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,470评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,550评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,806评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,951评论 3 407
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,712评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,166评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,510评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,643评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,306评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,930评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,745评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,983评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,351评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,509评论 2 348

推荐阅读更多精彩内容