Android卡顿相关原理和排查工具

Android卡顿优化思考

前言

大家在平时使用手机的时候,是否遇到过我的网络明明很好,怎么一个页面半天跳转不过去,或者是,经常看到在玩王者荣耀和刺激战场时,画面都卡成ppt了,完全是ppt游戏。画面流畅度不够,掉帧特别严重。

基础知识

造成卡顿的原因可能有千百种,不过最终都会反映到 CPU 时间上。我们可以把 CPU 时间分为两种:用户时间和系统时间。

。用户时间就是执行用户态应用程序代码所消耗的时间;系统时间就是执行内核态系统调用所消耗的时间,包括 I/O、锁、中断以及其他系统调用的时间。

1. CPU 性能

我们先来简单讲讲 CPU 的性能,考虑到功耗、体积这些因素,移动设备和 PC 的 CPU 会有不少的差异。但近年来,手机 CPU 的性能也在向 PC 快速靠拢,华为 Mate 20 的“麒麟 980”和 iPhone XS 的“A12”已经率先使用领先 PC 的 7 纳米工艺。也因此在开发过程中,我们需要根据设备 CPU 性能来“看菜下饭”,例如线程池使用线程数根据 CPU 的核心数。

2. 卡顿问题分析指标

出现卡顿问题后,首先我们应该查看 CPU 的使用率。怎么查呢?我们可以通过/proc/stat得到整个系统的 CPU 使用情况,通过/proc/[pid]/stat可以得到某个进程的 CPU 使用情况。

首先看下手机相关的cpu信息

cat /proc/cpuinfo

4551 (com.xxx.test) S 762 761 0 0 -1 1077936448 (1~9)
138821 2989 651 0 1161 399 3 2  (10~17)
10 -10 81 0 180732865 1463504896 28512 18446744073709551615 (18~25)
1 1 0 0 0 0 4612 0 38136 18446744073709551615 0 0 17 0 0 0 0 0 0 0 0 0 0 0 0 0 0
解释
  1. pid: 进程ID.
  2. comm: task_struct结构体的进程名
  3. state: 进程状态, 此处为S
  4. ppid: 父进程ID (父进程是指通过fork方式,通过clone并非父进程)
  5. pgrp:进程组ID
  6. session:进程会话组ID
  7. tty_nr:当前进程的tty终点设备号
  8. tpgid:控制进程终端的前台进程号
  9. flags:进程标识位,定义在include/linux/sched.h中的PF_*, 此处等于1077952832
  10. minflt: 次要缺页中断的次数,即无需从磁盘加载内存页. 比如COW和匿名页
  11. cminflt:当前进程等待子进程的minflt
  12. majflt:主要缺页中断的次数,需要从磁盘加载内存页. 比如map文件
  13. majflt:当前进程等待子进程的majflt
  14. utime: 该进程处于用户态的时间,单位jiffies,此处等于166114
  15. stime: 该进程处于内核态的时间,单位jiffies,此处等于129684
  16. cutime:当前进程等待子进程的utime
  17. cstime: 当前进程等待子进程的utime
  18. priority: 进程优先级, 此次等于10.
  19. nice: nice值,取值范围[19, -20],此处等于-10
  20. num_threads: 线程个数, 此处等于221
  21. itrealvalue: 该字段已废弃,恒等于0
  22. starttime:自系统启动后的进程创建时间,单位jiffies,此处等于2284
  23. vsize:进程的虚拟内存大小,单位为bytes
  24. rss: 进程独占内存+共享库,单位pages,此处等于93087
  25. rsslim: rss大小上限

Linux环境下进程的CPU占用率

继续刚刚讲的CPU使用率,以上这个文章有给我们的列出CPU使用率的计算方式,分为单核和多核计算。
如果 CPU 使用率长期大于 60% ,表示系统处于繁忙状态,就需要进一步分析用户时间和系统时间的比例。对于普通应用程序,系统时间不会长期高于 30%,如果超过这个值,我们就应该进一步检查是 I/O 过多,还是其他的系统调用问题。

Android 是站在 Linux 巨人的肩膀上,虽然做了不少修改也砍掉了一些工具,但还是保留了很多有用的工具可以协助我们更容易地排查问题,这里我给你介绍几个常用的命令。例如,top 命令可以帮助我们查看哪个进程是 CPU 的消耗大户;vmstat 命令可以实时动态监视操作系统的虚拟内存和 CPU 活动;strace 命令可以跟踪某个进程中所有的系统调用。

除了 CPU 的使用率,我们还需要查看 CPU 饱和度 。CPU 饱和度反映的是线程排队等待 CPU 的情况,也就是 CPU 的负载情况。

CPU 饱和度首先会跟应用的线程数有关,如果启动的线程过多,容易导致系统不断地切换执行的线程,把大量的时间浪费在上下文切换,我们知道每一次 CPU 上下文切换都需要刷新寄存器和计数器,至少需要几十纳秒的时间。

我们可以通过使用vmstat命令或者/proc/[pid]/schedstat文件来查看 CPU 上下文切换次数,这里特别需要注意nr_involuntary_switches被动切换的次数

 cat /proc/4551/sched  (4551 ->pid)
 nr_voluntary_switches: 主动上下文切换次数,因为线程无法获取所需资源导致上下文切换,最普遍的是IO。 
 nr_involuntary_switches: 被动上下文切换次数,线程被系统强制调度导致上下文切换,例如大量线程在抢占CPU。 
 se.statistics.iowait_count:IO 等待的次数 
 se.statistics.iowait_sum: IO 等待的时间
 

另外一个会影响 CPU 饱和度的是线程优先级,线程优先级会影响 Android 系统的调度策略,它主要由 nice 和 cgroup 类型共同决定。nice 值越低,抢占 CPU 时间片的能力越强。当 CPU 空闲时,线程的优先级对执行效率的影响并不会特别明显,但在 CPU 繁忙的时候,线程调度会对执行效率有非常大的影响。

需要注意是否存在高优先级的线程空等低优先级线程,例如主线程等待某个后台线程的锁。

以上是整个Andorid卡顿最根本的CPU相关知识。

Android卡顿排查工具

工欲善其事,必先利其器,我们要选择好针对我们app的分析工具,对此,Andorid也提供了相应工具,包括一些卡顿检测工具也很多,这里主要讲线下我们如何去检测并发现问题。我们调用Linux的命令比较繁琐,我们现在也可以获取到很多图形化的界面。

目前实现的流派主要有两种:

  • instrument

获取一段时间内所有函数的调用过程,可以通过分析这段时间内的函数调用流程,再进一步分析待优化的点。

  • sample

有选择性或者采用抽样的方式观察某些函数调用过程,可以通过这些有限的信息推测出流程中的可疑点,然后再继续细化分析。

一、TraceView

TraceView利用 Android Runtime 函数调用的 event 事件,将函数运行的耗时和调用关系写入 trace 文件中,所以他主要对应上述instrument流派的

使用方式:请在您希望系统开始记录跟踪数据的位置调用 startMethodTracing()
在调用中,您可以指定 .trace 文件的名称,系统会将它保存到一个特定于软件包的目录中,该目录专门用于保存目标设备上的永久性应用数据,与 getExternalFilesDir() 返回的目录相同,在大多数设备上都位于 ~/sdcard/ 目录中。此文件包含二进制方法跟踪数据,以及一个包含线程和方法名称的映射表。要停止跟踪,请调用 stopMethodTracing()

        mOpenTrace.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Debug.startMethodTracing("HughTrace");
            }
        });
        mCloseTrace.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Debug.stopMethodTracing();
            }
        });
        
        //在代码中调用
            private void onMainLongTimeWait(){
        try {
            Thread.sleep(1000);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }

也可以在对应生命周期进行处理。
接下来需要获取到生成的路径 sdcard/Android/data/com.xxx.app/
最后我们取出文件来放到profile来看下

在这里插入图片描述

我们可以看到这个方法调用时间和我们程序基本无差别
另外Traceview 对 release 包支持的都不太好,例如无法反混淆。所以在线下调试还是一个不错的选择,但是,traceview工具本身带来的性能开销过大,有时无法反映真实的情况。比如一个函数本身的耗时是 1 秒,开启 Traceview 后可能会变成 5 秒,而且这些函数的耗时变化并不是成比例放大。

二、Nanoscope

Uber开源的Nanoscope

由于Android studio在应用程序上启用检测后,它将阻止所有JIT / AOT编译代码运行。即,您的应用程序使用逐行解释器运行。这会使您的应用变慢几倍。除此之外,对于所检测的每个Java方法,还将执行数百条其他C ++行(某些示例行)。总体效果:使用AOSP内置的仪器时,您的应用程序运行速度降低了一个数量级。

为此,Nanoscope经过优化,可提供极低的配置开销和完整的仪器。使用Nanoscope时,您可以在应用程序和框架中看到每个Java方法的准确图片。

它的实现原理是直接修改 Android 虚拟机源码,在ArtMethod执行入口和执行结束位置增加埋点代码,将所有的信息先写到内存,等到 trace 结束后才统一生成结果文件。在使用过程可以明显感觉到应用不会因为开启 Nanoscope 而感到卡顿,但是 trace 结束生成结果文件这一步需要的时间比较长。

  • 限制
  1. 需要自己刷 ROM,并且当前只支持 Nexus 6P,或者采用其提供的 x86 架构的模拟器。(英趣只支持ARM架构,所以需要刷ROM比较麻烦)
  2. 默认只支持主线程采集,其他线程需要手动代码设置 Thread.currentThread().setName("START_TRACING");

三、systrace

systrace

systrace简介

systrace 是分析 Android 设备性能的主要工具。不过,它实际上是其他工具的封装容器:它是 atrace 的主机端封装容器,是用于控制用户空间跟踪和设置 ftrace 的设备端可执行文件,也是 Linux 内核中的主要跟踪机制。systrace 使用 atrace 来启用跟踪,然后读取 ftrace 缓冲区并将其全部封装到一个独立的 HTML 查看器中。

systrace 利用了 Linux 的ftrace调试工具,相当于在系统各个关键位置都添加了一些性能探针,也就是在代码里加了一些性能监控的埋点。Android 在 ftrace 的基础上封装了atrace,并增加了更多特有的探针,例如 Graphics、Activity Manager、Dalvik VM、System Server 等。

systrace 工具只能监控特定系统调用的耗时情况,所以它是属于 sample 类型,而且性能开销非常低。但是它不支持应用程序代码的耗时分析,所以在使用时有一些局限性

在考虑设备性能时,容量和抖动是两项重要指标。

  • 容量

容量是设备在一段时间内拥有的某种资源的总量。这种资源可以是 CPU 资源、GPU 资源、I/O 资源、网络资源、存储设备带宽或其他类似指标。

  • 抖动

抖动是一种随机的系统行为,会阻止可察觉任务的运行。通常是必须运行的任务,但可能对在任一特定时间运行没有严格的定时要求。因为抖动具有随机性,所以很难证明某一特定工作负载不存在抖动,也很难证明某已知抖动源是导致某个特定性能问题的原因。

  • 调度程序延迟
  • 中断处理程序
  • 驱动程序代码在抢占或中断被停用的情况下运行时间过长
  • 运行时间较长的软中断
  • 锁争用(应用、框架、内核驱动程序、Binder 锁、mmap 锁)
  • 文件描述符争用,低优先级的线程持有某个文件的锁,以防止高优先级线程运行
  • 在可能会延迟的工作队列中运行界面关键型代码
  • CPU 空闲转换
  • 记录
  • I/O 延迟
  • 创建不必要的进程(如 CONNECTIVITY_CHANGE 广播)
  • 释放内存不足所导致的页面缓存颠簸
systrace使用

如果您需要检查本机系统进程并解决帧丢失导致的界面卡顿问题,请在命令行中使用 systrace 或在 CPU Profiler 中使用经过简化的系统跟踪。CPU Profiler 提供了许多用于分析应用进程的功能。

输出10m app操作的trace文件

cd $ANDROID_HOME/platform-tools/systrace //需要先找到systrace这个目录

//Android_HOME的路径
//export ANDROID_HOME=/Users/macbook/Library/Android/sdk

找到对应的文件再继续执行以下内容

./systrace.py -t 10 sched gfx view wm am app webview -a com.hugh.basis
Starting tracing (10 seconds)
Tracing completed. Collecting output...
Outputting Systrace results...
Tracing complete, writing results

Wrote trace HTML file: file:///Users/macbook/Library/Android/sdk/platform-tools/systrace/trace.html

//这边用10s来测试Graphics 在操作中我调用了动画

在chrome中可以看到相关数据

在这里插入图片描述

  • Frames

在每个app进程,都有一个Frames行,正常情况以绿色的圆点表示。当圆点颜色为黄色或者红色时,意味着这一帧超过16.6ms(即发现丢帧),这时需要通过放大那一帧进一步分析问题。对于Android 5.0(API level 21)或者更高的设备,该问题主要聚焦在UI Thread和Render Thread这两个线程当中。对于更早的版本,则所有工作在UI Thread。

如图
在这里插入图片描述

  • Alert

Systrace能自动分析trace中的事件,并能自动高亮性能问题作为一个Alerts,建议调试人员下一步该怎么做。

比如对于丢帧是,点击黄色或红色的Frames圆点便会有相关的提示信息;另外,在systrace的最右上方,有一个Alerts tab可以展开,这里记录着所有的的警告提示信息。

systrace2 命令

python systrace.py [options] [category1] [category2] ... [categoryN]

  • options
options解释
-o 输出的目标文件
-t N, –time=N执行时间,默认5s
-b N, –buf-size=Nbuffer大小(单位kB),用于限制trace总大小,默认无上限
-k ,–ktrace=追踪kernel函数,用逗号分隔
–from-file=<FROM_FILE>从文件中创建互动的systrace
-l, –list-categories列举可用的tags
  • category2
category解释
gfxGraphics
inputInput
webviewWebView
wmWindow Manager
audioAudio
videoVideo
cameraCamera
resResource Loading

………… 等等

由于系统预留了Trace.beginSection接口来监听应用程序的调用耗时,那我们有没有办法在 systrace 上面自动增加应用程序的耗时分析呢?

我们可以通过编译时给每个函数插桩的方式来实现,也就是在重要函数的入口和出口分别增加Trace.beginSection和Trace.endSection。当然出于性能的考虑,我们会过滤大部分指令数比较少的函数,这样就实现了在 systrace 基础上增加应用程序耗时的监控。通过这样方式的好处有:

  • 可以看到整个流程系统和应用程序的调用流程。
  • 包括系统关键线程的函数调用,例如渲染耗时、线程锁,GC 耗时等

4.Simpleperf

Simpleperf 文档

Simpleperf主要增对native函数,它可用于分析Android应用程序和在Android上运行的本机进程。它可以在Android上分析Java和C ++代码。

Simpleperf 同时封装了 systrace 的监控功能,通过 Android 几个版本的优化,现在 Simpleperf 比较友好地支持 Java 代码的性能分析。

我们在Android 的profiler当中也可以看到Simpleperf的工具

5.Android Profiler

在 Android Studio 3.2 的 Profiler 中直接集成了几种性能分析工具,其中:

Sample Java Methods 的功能类似于 Traceview 的 sample 类型。

Trace Java Methods 的功能类似于 Traceview 的 instrument 类型。

Trace System Calls 的功能类似于 systrace。

SampleNative (API Level 26+) 的功能类似于 Simpleperf。

Profiler大大降低了开发者的使用门槛,但支持配置的参数也大大不如命令行,也在某些方面有些局限

6.Call Chart 和 Flame Chart

上述的分析工具都支持了Call Chart 和 Flame Chart的展示,接下来来讲讲这两种展示方式

  • Call Chart

Call Chart 是 Traceview 和 systrace 默认使用的展示方式。它按照应用程序的函数执行顺序来展示,适合用于分析整个流程的调用。Call Chart选项卡提供一个方法跟踪的图形表示,其中一个方法调用(或调用者)的周期和时间在水平轴上表示,而它的callees则显示在垂直轴上。

在这里插入图片描述

举一个最简单的例子,A 函数调用 B 函数,B 函数调用 C 函数,循环三次,就得到了下面的 Call Chart。

在这里插入图片描述

  • Flame Chart

同样举个例子 A->B->C

在这里插入图片描述

当我们不想知道应用程序的整个调用流程,只想直观看出哪些代码路径花费的 CPU 时间较多时,火焰图就是一个非常好的选择。

在这里插入图片描述

通过图我们就能直观的看到某个函数的调用CPU时间。可以优化我们的序列化方案或者一些io操作等等。

总结

上述的Android 4大工具中,像systrace、Simpleperf 也是利用 Linux 提供的机制实现,因此学习一些 Linux 的基础知识,对于理解这些工具的工作原理以及排查性能问题,都有很大帮助。在使用工具的同时我们要理解工具是如何实现的,对我们分析或是之后做性能监控的组件都有很大的借鉴意义

  • 5
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是几款常用的 Android 卡顿分析工具及使用介绍: 1. Systrace Systrace 是 Android SDK 提供的一款系统级分析工具,可以用于分析系统运行时的性能问题,包括 CPU 使用率、内存使用情况、线程运行情况、应用程序启动时间等等。使用 Systrace 可以帮助开发者快速定位应用程序卡顿的原因。 使用方法: 1)确保 Android SDK 中已经安装了 Systrace 工具。 2)在终端中输入以下命令,启动 Systrace 工具: ```python python systrace.py --time=10 -o mytrace.html gfx wm view sched freq idle ``` 其中,--time 参数指定采样时间,-o 参数指定输出文件名,gfx、wm、view、sched、freq、idle 参数指定要监控的系统事件。 3)在终端中输入以下命令,停止 Systrace 工具: ```python Ctrl + C ``` 4)使用浏览器打开输出的 HTML 文件,即可查看 Systrace 分析结果。 2. Traceview Traceview 是 Android SDK 提供的一款应用程序级分析工具,可以用于分析应用程序在运行时的性能问题,包括方法调用时间、内存使用情况、线程运行情况等等。使用 Traceview 可以帮助开发者定位应用程序卡顿的原因。 使用方法: 1)在应用程序代码中插入 Trace 开始和结束语句,例如: ```java Debug.startMethodTracing("mytrace"); // 要分析的代码逻辑 Debug.stopMethodTracing(); ``` 2)在 Android Studio 中打开应用程序项目,选择菜单栏中的 Run -> Profile 'app',打开 Profiler 工具。 3)在 Profiler 工具中选择 CPU 标签页,点击 Record 按钮开始录制 Trace 数据。 4)在应用程序中操作,触发卡顿问题。 5)点击 Stop 按钮停止录制 Trace 数据。 6)在 Profiler 工具中选择 CPU 标签页,点击 Open in Traceview 按钮,打开 Traceview 工具。 7)在 Traceview 工具中查看 Trace 数据,定位应用程序卡顿的原因。 3. Profiler Profiler 是 Android Studio 提供的一款性能分析工具,可以用于分析应用程序的性能问题,包括 CPU 使用率、内存使用情况、线程运行情况等等。使用 Profiler 可以帮助开发者快速定位应用程序卡顿的原因。 使用方法: 1)在 Android Studio 中打开应用程序项目,选择菜单栏中的 Run -> Profile 'app',打开 Profiler 工具。 2)在 Profiler 工具中选择 CPU 标签页,点击 Record 按钮开始录制性能数据。 3)在应用程序中操作,触发卡顿问题。 4)点击 Stop 按钮停止录制性能数据。 5)在 Profiler 工具中查看性能数据,定位应用程序卡顿的原因。 4. CTS (Compatibility Test Suite) CTS 是 Android SDK 提供的一款兼容性测试工具,可以用于测试应用程序的兼容性和性能。CTS 可以帮助开发者发现应用程序中的性能问题,包括卡顿、崩溃等等。 使用方法: 1)在 Android SDK 中安装 CTS 工具。 2)在终端中输入以下命令,运行 CTS 测试: ```python cts-tradefed run cts --plan CTS --device <device_id> ``` 其中,--plan 参数指定测试计划,--device 参数指定测试设备。 3)查看测试结果,定位应用程序卡顿的原因。 总之,Android 卡顿分析工具种类繁多,开发者可以根据实际需要选择合适的工具进行分析和优化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值