“工欲善其事,必先利其器“
一、LeakCanary
LeakCanary 想必大家都有了解一些,主要用于分析activity、fragment的内存泄露的问题。
在主module下的gradle导入如下依赖即可
dependencies {
// debugImplementation because LeakCanary should only run in debug builds.
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.7'
}
在安装测试app后,点击leak的图标进入leak应用,点击Dump Heap Now
即可,内存泄露引用链如下
二、BlockCanary
blockcanary 最新的一个版本是2017年发布的,已经很久没维护了,但是其原理还是值得借鉴的。作者文章
导入依赖方式:
// 当然也可以用implementation,建议用debug方式导入
debugImplementation 'com.github.markzhai:blockcanary-android:1.5.0'
需要注意的是由于涉及到读写文件,所以还需要声明对应的权限
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
创建一个application,并在Androidmanifest中使用
public class BlockCanaryApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
BlockCanary.install(this, new AppBlockCanaryContext()).start();
}
}
public class AppBlockCanaryContext extends BlockCanaryContext {
private static final String TAG = "AppBlockCanaryContext";
// block 时会回调
@Override
public void onBlock(Context context, BlockInfo blockInfo) {
super.onBlock(context, blockInfo);
Log.d(TAG, "onBlock: " + blockInfo.model);
}
//卡顿阀值 ,默认是1000 ms
@Override
public int provideBlockThreshold() {
return 200;
}
}
展示dump信息的页面基于LeakCanary界面修改,可以很清楚看到哪里卡了和卡的时长
三、Perfdog
Perfdog是由腾讯出品的移动平台性能分析工具,官网点我前往,工具首页如下,默认的功能有:FPS、CPU、memory三个维度的性能。
- 右下角可以扩展更多功能
- 点击右上角可以开始记录数据,再点一下可以保存到云平台。
在官网登录后就可以看到对应的详细数据
四、Profiler
Perfdog 只适合用于监控CPU,内存、FPS等情况,如果想具体排查问题,还是得用Android studio自带的Profiler
profiler支持CPU、memory、network、energy维度的分析。
1、CPU
在CPU下,点击record可以开始记录一段时间内的方法耗时情况
点击stop后,就可以看具体的执行耗时情况,比如在main线程中,clickView的方法耗时长达270ms,就可以结合代码做具体的耗时分析。
2、Memory
在Memory下,可以看到每个块所占用的内存大小,如果想具体看内存分配情况,可以点击顶部的“Allocation Tracking”
在点击stop后,就会进入如下页面
- 区域1:可以按照不同的归类来查看内存情况
- 区域2:每个类实例对应的内存分配情况,单位是byte。点击对应的分类,可以按照该分类的内存情况升序或者降序排列。
- 区域3:该实例对应的成员变量和引用链,对于分析内存泄露很有帮助
- 区域4:该类的实例列表,正常列表只有一个,如果有多个,有可能发生了内存泄露。
3、Network
可以测试网络的收发速度
五、命令
1、dumpsys meminfo
可以通过如下命令查看包为 com.example.kotlindemo
的内存信息(也可以查看PID对应的信息)
C:\Users> adb shell dumpsys meminfo com.example.kotlindemo
Applications Memory Usage (in Kilobytes):
Uptime: 5765033 Realtime: 5765033
** MEMINFO in pid 19462 [com.example.kotlindemo] **
Pss Private Private SwapPss Heap Heap Heap
Total Dirty Clean Dirty Size Alloc Free
------ ------ ------ ------ ------ ------ ------
Native Heap 28070 28012 32 148 92160 34457 57702
Dalvik Heap 0 0 0 0 8199 4100 4099
Stack 96 96 0 0
Ashmem 13 0 12 0
Gfx dev 3568 3568 0 0
Other dev 2 0 0 0
.so mmap 7331 388 3484 9
.apk mmap 168 0 20 0
.ttf mmap 143 0 60 0
.dex mmap 4965 12 3104 0
.oat mmap 219 0 0 0
.art mmap 8397 7728 348 84
Other mmap 107 4 0 0
EGL mtrack 24660 24660 0 0
GL mtrack 2684 2684 0 0
Unknown 31770 31732 4 27
TOTAL 112461 98884 7064 268 100359 38557 61801
App Summary
Pss(KB)
------
Java Heap: 8076
Native Heap: 28012
Code: 7068
Stack: 96
Graphics: 30912
Private Other: 31784
System: 6513
TOTAL: 112461 TOTAL SWAP PSS: 268
Objects
Views: 18 ViewRootImpl: 2
AppContexts: 4 Activities: 1
Assets: 9 AssetManagers: 0
Local Binders: 14 Proxy Binders: 33
Parcel memory: 9 Parcel count: 21
Death Recipients: 2 OpenSSL Sockets: 11
WebViews: 0
SQL
MEMORY_USED: 0
PAGECACHE_OVERFLOW: 0 MALLOC_SIZE: 0
这里的单位是kb,其中
- Pss Total:实际使用的内存,将跨进程共享页也加入进来,会比在profiler中的要大一些。(重点关注之一)
- Private Dirty:是应用独占内存大小,包含独自分配的部分和应用进程从Zygote复制时被修改的Zygote分配的内存页。(重点关注之一)
- Private clean:是已经映射持久文件使用的内存页,比如正在被执行的代码。
- Dalvik Heap:Dalvik 虚拟机分配的内存。
- Java Heap:java堆大小。
Objects
中显示持有对象的个数,从这里我们可以分析view、activity的个数。其中,可以通过看activity的个数判断是否发生内存泄漏。
2、systrace
2.1 基本用法
systrace
需要Python环境,在使用前请先配置好adb,并连接上手机。
window环境可以按照如下配置
- python 2.7 环境:用
py -2
或者py -3
切Python2和Python3 - six模块:安装命令
py -2 -m pip install six
- win32con模块:安装命令
py -2 -m pip install pypiwin32
Android SDK 工具软件包中提供该命令,对应路径是 android-sdk/platform-tools/systrace/
语法如下:
python2 systrace.py [options] [categories]
可用参数如下
命令和选项 | 说明 |
---|---|
-o file | 将 HTML 跟踪报告写入指定的文件。如果您未指定此选项,systrace 会将报告保存到 systrace.py 所在的目录中,并将其命名为 trace.html 。 |
-t N | 跟踪设备活动 N 秒。如果您未指定此选项,systrace 会提示您在命令行中按 Enter 键结束跟踪。 |
-b N | 使用 N KB 的跟踪缓冲区大小。使用此选项,您可以限制跟踪期间收集到的数据的总大小。 |
-k functions | 跟踪逗号分隔列表中指定的特定内核函数的活动。 |
-a app-name | 启用对应用的跟踪,指定为包含进程名称的逗号分隔列表。 |
-h | 显示帮助消息。 |
在执行完后,会自动生成一个html文件。可以用Chrome打开,在Chrome浏览器网址栏输入
chrome://tracing/
点击load 按钮,选择我们的trace文件即可
其中
- 区域1是总CPU的使用情况
- 区域2是指定进程的cpu使用情况
- 区域3是用于鼠标的控制功能,从上往下依次为点击、上下左右移动、点击上下拉缩放、框定时间区域。
- 顶部的processes可以选择你感兴趣的进程
- 右上角”?“可以查看操作信息
- metrics栏可以查看各项指标。
更多见浏览systrace报告
2.2 查看GC
在启动过程,要尽量减少 GC 的次数,避免造成主线程长时间的卡顿,特别是对 Dalvik 来说,我们可以通过 systrace 单独查看整个启动过程 GC 的时间。
python2 ./systrace.py dalvik -b 90960 -a com.sample.gc
对于GC的使用含义,可以参考调查RAM使用情况
2.3 自定义Trace
Android在关键系统回调添加了Trace,但是我们无法通过systrace准确获取到项目代码的耗时情况,因此需要在关键位置打label。
Android提供了android.os.Trace#beginSection()
和 android.os.Trace#endSection
这两个接口。比如想排查onCreate方法中,耗时的情况,可以通过如下代码实现
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 指定label为 "MainActivity_setContentView"
Trace.beginSection("MainActivity_setContentView")
method1()
method2()
Trace.endSection()
}
private fun method2() {
Thread.sleep(300)
}
private fun method1() {
Thread.sleep(200)
}
}
重点:要想使用自定义Label,就必须在gradle中,打开debuggable
buildTypes {
debug {
debuggable true
}
}
执行systrace,其中-a
表示开启指定包自定义Trace label的功能
$ python2 .\systrace.py -a com.example.asm
打开应用,进入指定页面后,退出systrace。用chrome打开trace.html 文件。如下所示,可以看到systrace对性能的影响很小。
但是由于开启了debuggable,与正式的release版本在性能上会有较大差异,如果我们想尽可能的还原真实情况,那必须在非debuggable模式下执行。我们可以通过如下方式绕过debuggable的限制
public class BaseApplication extends Application {
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
try {
Class<?> trace = Class.forName("android.os.Trace");
Method setAppTracingAllowed = trace.getDeclaredMethod("setAppTracingAllowed", boolean.class);
setAppTracingAllowed.invoke(null, true);
} catch (Exception e) {
e.printStackTrace();
}
}
}
如果自定义label,再配上插桩,那就能最接近真实情况下,详细分析每个方法的耗时情况。
插桩实现和库可以参考TraceFix
3、Perfetto
Perfetto 是 Android 10 中引入的全新平台级跟踪工具,你可以在perfetto界面中打开这些跟踪
或者可以通过命令方式打开
cd /path-to-traces-on-my-dev-machine
systrace --from-file trace-file-name{.ctrace | .perfetto-trace}
更多见系统跟踪
4、Debug接口
4.1 获取Trace文件
Android为我们提供了Debug工具,可以获取指定路径的trace文件,我们只需要在特定的位置加入如下代码,即可获取对应的trace文件
// 设置开始记录方法调用情况
Debug.startMethodTracing("/sdcard/debug.trace");
// 结束记录方法调用情况
Debug.stopMethodTracing();
将trace文件pull出来后,直接把文件拖拽到Android studio中即可。区域1为各个线程的耗时情况,区域2 为对应的火焰图。
4.2 获取Hprof文件
通过如下获取hprof文件,需要注意如下代码十分的耗性能
Debug.dumpHprofData("/sdcard/dump.hprof")
pull出来用Android studio打开如下
5、查看RAM
$ cat /proc/meminfo | head -n 4
MemTotal: 5861796 kB // 总共内存
MemFree: 90268 kB // 空闲内存
MemAvailable: 3392216 kB // 可用内存(即剩余运行内存)
Buffers: 1049552 kB // 缓存
6、查看CPU——top
$ top
Tasks: 743 total, 2 running, 737 sleeping, 0 stopped, 0 zombie
Mem: 5861796k total, 5683832k used, 177964k free, 1057640k buffers
Swap: 2621436k total, 478088k used, 2143348k free, 1994168k cached
800%cpu 21%user 4%nice 8%sys 764%idle 1%iow 2%irq 1%sirq 0%host
PID USER PR NI VIRT RES SHR S[%CPU] %MEM TIME+ ARGS
5407 u0_a99 20 0 1.7G 69M 45M S 19.0 1.2 13:15.58 com.miui.voiceassist:voice_trigger
13736 shell 20 0 14M 3.0M 1.6M R 3.3 0.0 0:00.25 top
704 audioserver 20 0 41M 6.3M 5.9M S 2.3 0.1 2:09.01 android.hardware.audio@2.0
其中
- 800%cpu:即cpu核数,这里800%指有8个核。
- 21%user:即用户态的占比占单核的21%
- 4%nice:优先级为负数的进程占用的cpu
- 8%sys:处于核心态的cpu占比
- 764%idle:CPU处于空闲状态时间比例。一般而言,idel + sys + user + nice 约等于cpu
- 1%iow:IO等待的CPU占比
- 2%irq:硬中断的cpu占比
- 1%sirq:软中断的cpu占比
- PID:即进程id
- PR:优先级,越低优先级越高。
- VIRT:虚拟内存大小,包括:进程使用的库、代码、数据等。
- RES:常驻内存,当前进程使用的内存大小,不包含swap out
- SHR:除了自身进程的共享内存,也包括其他进程的共享内存。
- %CPU:即该进程占用的CPU占比。
- %MEM :即该进程占用的内存占比。
7、查看CPU——dumpsys
可以查看每个进程所用的CPU百分比
$ dumpsys cpuinfo
CPU usage from 476124ms to 176038ms ago (2021-10-17 13:35:57.070 to 2021-10-17 13:40:57.156):
17% 5407/com.miui.voiceassist:voice_trigger: 16% user + 0.4% kernel / faults: 10382 minor
2.6% 704/android.hardware.audio@2.0-service: 0.7% user + 1.9% kernel
更多dumpsys命令见android调试——教你用dumpsys命令调试
六、GPU
1、渲染速度
可以通过 设置-》开发者选项-》监控下的GPU呈现方式-》在GPU 渲染模式分析
对话框中,选择在屏幕上显示为竖条
或者参考App性能调试详解 用命令打开。
// Possible values:
// "true", to enable profiling
// "visual_bars", to enable profiling and visualize the results on screen
// "false", to disable profiling
// @see #PROFILE_PROPERTY_VISUALIZE_BARS
adb shell setprop debug.hwui.profile #{value}
效果如下
其中, Android 6.0 及更高版本的设备时分析器输出中某个竖条的每个区段如下所示:
下表显示的是 Android 4.0 和 5.0 中的竖条区段。
2、过渡绘制
可通过 设置-》开发者选项-》硬件加速渲染-》调试 GPU 过度绘制-》选择显示过度绘制区域。
或者使用命令打开
adb shell setprop debug.hwui.overdraw show
效果如下:
Android 将按如下方式为界面元素着色,以确定过度绘制的次数:
后记
到目前为止,Google一直为Android提供新的调试工具,从monitor(已经被打入冷宫)到profiler,从systrace到Perfetto,Android studio也一直在迭代更新。目的就是让开发者能开发出更优秀的产品,也愿各位大佬不辜负Google的期望!共勉!!
——Weiwq 于 2021.08 广州