首页 > 代码库 > APP性能分析工具
APP性能分析工具
本文基本翻译自Facebook工程师的文章
Speed up your app,也加入了自己的一些内容。
会介绍以下几个主题
- Systrace
- Traceview
- Memory Profiling
- Allocation Tracker
- GPU Profiling
- Hierarchy Viewer
- Overdraw
- Alpha
- Hardware Acceleration
Systrace的功能可以在AS的DDMS中找到,但不太稳定,所以这里只介绍命令行模式。
另外Systrace只能分析概况,不能定位问题位置,不太感兴趣的朋友也可以直接跳到第二节Traceview。
在终端(我的环境是MAC)中先进入sdk/platform-tools/systrace目录
cd /Users/apple/Documents/Android/sdk/platform-tools/systrace
然后执行Systrace的命令是
python systrace.py --time=10 -a com.duotin.fm -o mynewtrace.html sched gfx view wm
–time=10 表示记录10秒
更多参数说明请查看官网
执行完打开生成的mynewtrace.html文件
呈现这样的界面
点击第一列的三角形警示图标或者第二列的圆形警示图标,都可以查看警示详情,点击效果分别如下
三角形警示图标,每个图标代表一个警示,点击后查看警示详情。
圆形警示图标,每个图标代表一个frame,显示红色或者黄色表示此frame的时间已经超过16.6 millisecond per frame的标准,会导致界面失帧。点击后查看这个frame所有的警示。
此时按”M”快捷键可以高亮当前选中的frame。“A”和”D”分别为左移和右移视图,”W”放大,”S”缩小。
此外,点击各颜色块,可以查看各颜色块的详情。
点击上图中的第一个Alert: Inflation during ListView recycling
显示详情
可以看出Inflation during ListView recycling的执行时间是32ms(远远超过了16ms的限制),共5个item,平均到每个item为6ms。通过点击该frame范围内的颜色块,可以查看各个方法的详情。
我们再点击一个圆形警示图标并高亮
顶部显示此frame耗时19ms, 点击右下方的Alert,显示有 Scheduled delay。
Scheduled delay 是指告诉CPU执行任务,但CPU太忙了,任务被延迟执行了。
点击下面的一个颜色块,显示如下
Wall duration 是指此颜色块代表的方法从开始到结束的耗时
CPU duration 是指 CPU的执行时间
可以发现CPU duration只有4ms,但Wall duration有18ms。
延迟这么严重,我们来看看原因。
在选中的颜色块上方,我们看到四个CPU都被颜色块填充,表示此时4个CPU都有活干,很忙。
我们选中一个CPU的颜色块
可以发现占用CPU的应用是com.udinic.keepbusyapp
恩,对Systrace的介绍到此结束了,虽然还有些没讲,但Systrace的确只能看个概览。
且慢,再加送一个tip, 点击右边栏的Alerts, 你能看到所有的Alerts.
通过这张图我分析出
inefficient view alpha usage数量最多。Inflation during ListView recycling影响时间最长,耗时长达52ms
Traceview能够在方法层面上分析APP的性能,非常强大。
可以通过,命令行或者GUI启动,我用的是GUI启动,点击AS的Android Device Monitor, 点击Devices栏目下面的 Start Method Profilling , 在对话框中选择,(我选的是trace Based Profilling,表示实时分析,会比较慢,但分析结果详细),操作APP, 分析结束的时候,点击同一个图标即可。更多操作请访问官网
先看下界面
列名 | 意义 |
---|---|
Name | 方法名,每个方法的颜色都不一样。 |
Inclusive CPU Time | 此方法占用CPU的时间,Inclusive指包括调用的方法所 |
Exclusive CPU Time | 此方法所占用CPU的时间,Exclusive 指不包括调用的方法 |
Inclusive / Exclusive Real Time | Real Time指方法从开始到结束消耗的时间,跟Systrace中的Wall duration一个意思。 |
Calls+Recursion | 此方法被调用了多少次+多少次是递归调用 |
Calls / Total | 子方法被此父方法调用的次数/子方法被调用的总次数 |
点击某条目下的parent 或者 child 方法时,会跳到该方法的条目。
想找出最影响性能的方法,可以点击Exclusive CPU time一栏,找出消耗时间最长的几个方法。如果是应用的方法,直接看可不可优此方法。如果是系统方法,通过查看其父方法,追溯至应用方法。
而查看子方法,可以看出此方法到底做了什么。
如果要找UI卡顿的原因,可以从 getView(), View#onDraw() 等方法入手。
方法执行时占用了CPU,所以执行时间过长会造成UI渲染被延迟,从而应用不流畅。而GC同样会占用CPU,AS也同样提供了查看GC的工具:
点击AS中的Android Monitor, 选中Memory | CPU 一栏, 界面如下
如图所示,小幅的内存下降一般就是发生了GC。
点击左侧的Heap dump,会生成内存中的所有对象的快照。
列名 | 意义 |
---|---|
Total Counts | 内存中该类的对象个数 |
Heap Count | 在该堆中该类的对象个数,左上角可以选择App heap或Zygote heap |
Sizeof | 单个对象占用的Shallow Size |
Shallow Size | 所有对象所占用的Shallow Size |
Retained Size | 所有对象所占用的Retained Size,即GC后会释放的内存 |
instance | 该类一个具体的对象 |
Reference Tree | 引用这个对象的父对象,点击父对象,展开这些父对象的父对象 |
什么是Shallow Size 和Retained Size
选中一行,点击右侧的一个instance,可以在下方看到Refrence Tree界面
在图中可以看出MemoryActivity的一个instance在ListenerManager中被引用了。如果MemoryActivity已经不在Activity栈中了,这样的引用就是内存泄漏。另外一个检查内存泄漏的工具是leakcanary
通过查看Retained Size和Reference Tree,我们可以知道哪些对象占用了较多的内存,对象间的引用关系,进而分析是否可以优化数据结构,减少引用关系,以减少内存占用和GC频率。
Memory | CPU一栏左侧的另一个按钮Allocation Tracker也是用于分析内存占用。点击一次表示开始记录,再次点击表示停止记录。
在结果页面的左上方点击饼状图。
可以选择 group by Allocator,即按对象划分。
或者 group by method
Allocator下面的饼状图最外围的是具体的类,内部的是包名。图中可以看出包或者对象占用的内存大小或者个数,面积越大,占用或者个数越多。选择size可以查看占用内存最多的对象,选择count可以查看以及个数最多的对象。前者我们可以试着优化类,后者我们可以尝试建立一个Object pool来复用对象。
从group by method可以看出,decode方法占用的总内存达10.91M, 就有可能是方法内新建了太多对象,可以往这方面优化。
内存方面的tips:
1. Enums Enums比int占的内存大得多,而且有替换方案@IntDef, 所以除了某些情况,比如你需要强制指定类型,不然的话int会更节省内存
2. 自动装箱 自动装箱指从基本类型自动转换到对象形式的(比如int到Integer),鉴于基本类型使用的场景和次数都较多,所以需要尽量避免使用其自动装箱的形式。
3. HashMap vs ArrayMap / Sparse*Array 如果我们需要使用int作为Map的值,可以使用SparseIntArray,比起使用HashMap对int自动装箱,要省内存的多。如果要使用Object作为Map的key,除了HashMap,你也可以考虑使用ArrayMap,功能和HashMap一样,但更省内存,点击了解原理。尽管时间性能上HashMap更胜一筹,但除非你要存储1000个以上对象,否则他们使用起来几乎一样快。
4. 注意Context对象 因为Context在开发中的使用场景较多,所以最容易造成内存泄漏。Activity本身是一个heavy的对象,为了避免内存泄漏,可以穿ApplicationContext的话,就不要传Activity了。
5. 避免非静态内部类 非静态内部类隐式持有外部类的引用,所以如果外部类不再被需要,但内部类仍在使用状态,就造成了内存泄漏。特别是Activity类,在定义内部类的时候尽量定义成static的。
首先在手机的开发者选项页面,点击GPU呈现模式分析(Profile GPU rendering),选中“In adb shell dumpsys gfxinfo” 然后在AS的Android Monitor 界面选中GPU一栏,确保左上方的暂停按钮没有选中,此时AS就开始按照选定的包名显示GPU情况了。每一个条直线表示UI渲染中的一帧,不同的颜色表示不同的绘制阶段。
- Draw(蓝色) 执行的是View#onDraw()方法。这个阶段的工作是创建DisplayList对象,这些对象稍后将被转换成OpenGL命令,传送到GPU。如果蓝色较长,一般是因为较复杂的view, 或者短时间内invalidate了较多的view
- Prepare (紫色) Lollipop才引入的阶段,用于加快UI渲染,在线程RenderThread中执行。这个阶段的任务是将第一步产生的display lists转换成OpenGL命令,并传送到GPU。此时UI thread将继续处理下一帧。UI Thread给RenderThread传递所需的资源产生的耗时也记录在此阶段中。当有大量的资源要传递,比如很多/很heavy的display lists,这个阶段耗费的时间会增多。
- Process(红色) 处理display lists,产生OpenGL命令,较多或者较复杂的display lists会使此阶段耗时增加,因为很多view将被redraw。view被redraw的情况有invalidate或者之前覆盖在上面的view现在被移走了。
- Execute (黄色) 将OpenGL命令发送给GPU, 这是个阻塞方法,因为CPU通过buffer将OpenGL命令发送给GPU, 当处理完毕返回空的buffer。buffer的数量有限,所以当GPU很忙,buffer也用完了,CPU就需要等待GPU处理完返回一个空的buffer,才能继续发送OpenGL命令。因此如果这个阶段耗时较多,一般是因为在绘制复杂的view。
在 Marshmallow 版本,增加了更多的颜色
翻译了一半了,待续
APP性能分析工具