Android查看每个线程CPU占用情况,以及工作内容分析
简介
Android应用开发阶段,有时候会发现应用占用CPU特别高,本文将针对这种场景提出解决方案,排查Java线程问题以及Native线程问题。
PS: 本文使用的Android Studio版本为3.1
查看CPU占用率的命令
adb shell top
使用参数:
Usage: top [ -m max_procs ] [ -n iterations ] [ -d delay ] [ -s sort_column ] [ -t ] [ -h ]
-m num Maximum number of processes to display. 最多显示几个进程,top会自动进行排序,比如让CPU占用率高的进程在前
-n num Updates to show before exiting. 刷新次数
-d num Seconds to wait between updates. 刷新间隔,可以输入小数即代表毫秒级间隔
-s col Column to sort by (cpu,vss,rss,thr). 选择以哪一项进行排序
-t Show threads instead of processes. 显示线程
-h Display this help screen.
下面是adb shell top -m 10 -t -d 2命令打印
User 62%, System 27%, IOW 0%, IRQ 4%
User 623 + Nice 15 + Sys 286 + Idle 57 + IOW 0 + IRQ 33 + SIRQ 8 = 1022
PID TID USER PR NI CPU% S VSS RSS PCY Thread Proc
4014 9996 u0_a280 20 0 18% R 2524268K 319356K ta Thread-61 com.xxxxxx
4014 4369 u0_a280 20 0 7% R 2524268K 319356K ta xxxxxxxxxxxxxxx com.xxxxxx
4014 10067 u0_a280 12 -8 5% R 2524268K 318748K ta xxxxxxxxxxxxxxx com.xxxxxx
1162 1620 system 20 0 3% S 2515560K 274232K fg Binder:1162_3 system_server
1162 2274 system 20 0 3% S 2515560K 274232K fg Binder:1162_8 system_server
4014 4136 u0_a280 10 -10 2% S 2524268K 319356K ta RenderThread com.xxxxxx
9865 9865 shell 20 0 2% R 9108K 3112K fg top top
4014 4064 u0_a280 21 1 2% S 2524268K 319356K ta xxxxxxxxxxxxxxx com.xxxxxx
4014 4014 u0_a280 16 -4 2% S 2523236K 319092K ta xxxxxxxxxxxxxxx com.xxxxxx
1162 2384 system 20 0 2% S 2515560K 274232K fg Binder:1162_A system_server
各列含义
PID:略
PR:在android N之前代表运行在哪个核上,在android N上代表优先级,当然可能设备厂商会进行自定义
CPU%:略
S:运行状态
#THR:线程数
VSS:Virtual Set Size 虚拟耗用内存(包含共享库占用的内存)
RSS:Resident Set Size 实际使用物理内存(包含共享库占用的内存)
PCY:调度策略优先级,SP_BACKGROUND/SP_FOREGROUND
UID:进程所有者的用户id
Thread:线程名称
Name:进程名
可以看出,CPU占用最高的是前两个,其中com.xxxxxx是我自己的应用包名。从Thread那一列可以看出Thread的名称。
PS:android 8.0以上的系统,TOP命令有所改变,但是大同小异,用adb shell top -H就行,具体可以查看帮助文档
查看线程工作
知道了具体哪个线程占用CPU高之后,再使用Android Studio的android profiler开启method record。导出结果后查看线程方法调用即可。
android profiler无法导出
有时候,你发现一个线程CPU占用很高,但是,通过android profiler追踪调用栈的时候,显示下面这个结果: No data available for the selected thread.
此时,可以通过打断点debug调试,导出所有线程的调用栈,比如在主线程的一个按钮点击事件里面,打个断点,当程序跑到断点处时,调出Debug
面板,点击左侧的Get thread dump
按钮。
点击之后,会出现所有现成的调用栈列表,找到消耗CPU高的线程,在右侧查看其调用栈
如果出现上图的情况,调用栈并没有你的项目代码的时候,可以看看调用栈调用的对象有哪些,比如截图中有TimerThread
类,则此时,可以把java堆内存导出来,然后找TimerTread
对象,并查看其引用,看看能不能找到项目中的类对象对其的引用。
Native线程问题
如果,在profiler里能看到某一个线程CPU占用非常高,而通过上面这些方法仍然无法定位到问题,那么,可以确定,这个线程是Native层创建的,此时应该排查各个so是否有线程问题。
如何查看到native线程的问题,需要Android系统在8.0及以上,Android Studio 版本在3.1及以上。
在8.0手机上复现线程问题之后,使用Android Studio的CPU Profiler,查看native线程调用栈。
关于CPU Profiler使用
大家参考Google爸爸的文档:https://developer.android.com/studio/profile/cpu-profiler
总结:
整体流程比较清晰
1. 知道哪个线程消耗的CPU高(adb shell top命令)
2. 尝试使用android profiler来获取线程调用栈
3. 第2步如果不行,则采用断点调试打印所有线程调用栈的方式获取调用栈
4. 第3步如果看不出东西,则把java堆内存导出来,查看第3步中的类对象的引用关系,最后定位到自己项目里的类对象中
5. Native层的线程泄露,通过Java堆栈,或者Java虚拟机导出线程信息的方式是定位不到到问题的
转载请注明出处:https://blog.csdn.net/a740169405/article/details/79046211