写在前面
目前,业内的跨端框架不少:react-native,weex,hippy,taro等等。采用跨端框架的项目一般都依附于客户端而存在,由前端同学开发。为了避免客户端发生crash,相对于传统浏览器上的前端页面开发,跨端项目对于内存管理、CPU利用率的要求更高。
本文将以某App上的小说阅读器为例,讲解在对项目代码完全不了解的情况下,如何进行内存问题的优化。
小说的用户场景比较简单:打开一部小说,一直滑动阅读即可。
前期工作
第一步,我们需要工具,来量化项目的内存表现。perfdog 是常用的工具之一。使用很简单,手机连上电脑,打开perdog,打开想要测试的应用,进行操作,就可以看到实时的内存和CPU表现了。
由于虚拟内存达到某一个临界值后,会导致APP直接crash;所以需要明确最重要的衡量指标:1、一顿操作后的虚拟内存增量。2、峰值虚拟内存;
开始优化
优化方向
前文已经讲到,我们优化的衡量指标是:虚拟内存峰值、增量
造成内存增加的原因主要是:
1、业务需要,在内存里面储存了业务数据
2、内存泄露导致无法按预期进行GC
对于第一点,并不具有普适性,需要具体情况具体分析。第二点是我们优化的着重发力点。
优化步骤
以react为例,说起内存问题优化(避免内存泄露),方法都耳熟能详:
1、定时器需要清除 ; 2、注意闭包的使用;3、组件销毁时需要清除对其的引用;4、使用发布订阅模式时,组件销毁时,需要移除事件监听;5、注意全局变量的使用。。。。。
但是在对业务代码完全不熟悉,并且业务代码量巨大时,直接从几万行代码入手或许不是最好的选择。
1、采集内存增量
怎么找到可优化或者说内存泄露的地方呢?perfdog可以帮助我们查看实时内存,但是没有办法细化到具体的内存。作为前端开发,我们可以利用常用的chrome inspector的内存快照工具帮助我们进行分析。
我们想要测试的是内存增量。在开始操作前,先记录一次内存快照。接着模拟用户,进行一系列操作。操作完成后,手动触发一次gc,再记录一次内存快照。查看这两次快照之间的差异,来判断内存增量的由来。
至此,我们就可以看到我们的操作所带来的内存增量了。接下来开始进行分析。
2、分析内存增量
以本文提及的小说场景为例,在滑动了小说几个章节后,发现某组件对象数量增加了很多,但是一个也没有被删除。点开其中一个对象实例,查看其引用链,发现被其外层组件所引用了。
通过关键变量名,可以迅速定位到问题代码,进行修复。最终发现父组件中,有一个变量持有了所有的子组件,导致被销毁的子组件无法被GC,造成泄漏。
再举一个例子:
从上面的图中我们可以发现,Promise实例数也是只增加,不减少。点开其中一个Promise进行排查,发现大量Promise一直处于pending状态。这里肯定是有问题的。
其余不少组件也存在实例数只增加不减少的情况,点开其中一个组件发现:
组件内部引用的Promise一直pending,导致该组件也被系统一直持有了,无法释放,一切都是卡住的Promise的锅。
通过查看Promise的持有链,最终发现是由于一个Bug,导致了部分Promise一直无法resolve或reject,导致了内存泄露。修复Bug后,问题解决。
这里举了两个例子,说明了如何利用内存快照进行问题分析,总结一下:
拿到内存增量后,我们需要重点关注那些新增数量特别多,但是删除数量特别少的对象,很可能其中就有内存泄露,或者有明显的优化空间。
3、采集数据、分析优化效果
优化完成后,如何衡量优化效果呢?用数据说话。chrome inspector一般是用来调试的,严谨的采集数据需要借助其他工具。文章开头我们介绍了perfdog的使用方法,但是这里用人工来测试是不太现实的,我们可以使用简单的工具来辅助我们进行测试。
以Android为例,
1、首先安装adb工具,安装教程:https://www.jianshu.com/p/1b3fb1f27b67
2、接着连接手机,切换到想要测试的场景,使用 adb shell 来辅助测试。
以小说阅读场景为例,使用下面的命令可以控制测试手机自动翻页:
adb shell "input swipe 1000 1300 50 1300 1000"
3、然后我们可以使用Node.js写一个简单的脚本,就可以开启简陋的“自动化”测试了。下面就是一个自动左右滑动小说1000页的简易脚本
4、再结合perfdog,我们就可以得到内存和CPU的表现数据了。perfdog可以将数据导出成excel文件,开始采集数据时,点击perfdog右上角的开始按钮,结束时点击暂停按钮,即可导出数据。数据里面包括了每一秒钟的CPU、内存表现。
最后,通过人工或脚本的方法,便可以很方便的统计出虚拟内存增量,峰值虚拟内存,最终产出优化效果报告。
注意,采集数据的时候,需要等待客户端内存稳定后再开始,否则对最后的结果会产生较大影响。并且,每一次测试,需要固定测试路径,排除其他变量带来的干扰。
写在后面
本文主要是从一个前端工程师的角度,以基于跨端框架的项目为例,说明了如何实施一次内存问题优化。更进一步,可以和客户端同学配合,达到更加深度的优化效果。
在对项目代码完全不了解的情况下,我们可能无法直接从代码入手,一行一行死磕。可以借助工具,按照本文的思路进行分析,然后再对症下药。这是一项有迹可循的系统性工程。至于网络上的大部分优化宝典,更多的是作用于开发阶段。在开发时就应该养成良好的编码习惯和意识,最大限度的避免问题的发生。