前言
之前我所担任的项目运用的是购买的制品安卓设备,所以一向没有什么问题。
不久之前,老板决定不再购买制品设备,而是自己规划制作安卓硬件设备。
可是替换硬件之后,运转同一个 APP 的同一个版别会呈现卡顿的现象。
并且开机时刻越长该现象越明显,当开机时刻达到必定时刻后,甚至卡顿到完全没法运用。
而且卡顿时不仅是咱们的 APP 卡顿,而是整个体系都在卡顿,这明显是散热有问题。
可是老板可不会听咱们的所谓“明显”,凡事都需求拿出依据。
所以就有了这篇文章的内容。
基础常识
监控手机的运转性能运用多种方法都能够完成。
例如,能够运用 Android Studio 的 Profile 东西直接录制。
也能够下载其他第三方 APP 来监控记载。
可是,由于咱们这儿的背景是需求检查形成体系卡顿的原因,所以可想而知,到后期时整个体系运转有多吃力,此刻还挂着第三方 APP 的话,大概率会被强杀进程,或者干脆直接卡死(别问我怎样知道的)。
而运用 Profile 的话,问题在于本身这个东西在数据量大时就非常卡,而咱们需求做的监控是少则需求录制一天时刻的,到时估计录制出来的文件都打不开了(别问我怎样知道的x2)。
所以,咱们这儿挑选直接运用 ADB 运转 shell 指令获取需求的数据,然后保存下来。
因而,在开端之前,咱们需求了解一些基础常识,信任 ADB 的运用作为安卓开发的大伙都不会生疏吧。
咱们这儿就简略介绍几个接下来可能会用到的 ADB 常用常识。
获取当时衔接的设备:
adb devices
回来:
List of devices attached
78cb57bd device
其间前面的 “78cb57bd” 为 transport id ,在咱们同时衔接多个设备时能够运用 -t transport id 指定发送指令的设备。
在设备上履行 shell 指令
adb shell command
其间的 command 即为需求履行的 shell 指令,例如: 在设备上履行 ls
指令:
adb shell ls
可是我比较懒,不想每次都打这么长的指令,那咱们就能够经过:
adb sehll
进入到设备的终端 shell 中,此刻履行 shell 指令就不需求加上 adb shell
前缀了。
留意:接下来的指令其实都不是 adb 指令了,而是 shell 指令。
获取当时设备已装置的运用包名
pm list packages
获取当时运转的进程列表
ps -e -o PID,NAME
这个主要是用来获取运用的 pid 。
其间 ps 表明获取当时运转的进程,-e
表明输出一切进程,-o
表明指定输出内容,这儿咱们指定只输出 pid 和名称(即一般运用的包名)。
需求的指令
获取内存信息
能够运用指令 dumpsys meminfo
输出完好的内存信息:
cas:/ $ dumpsys meminfo
Applications Memory Usage (in Kilobytes):
Uptime: 5209932538 Realtime: 7380312852
Total RSS by process:
643,928K: system (pid 1633)
343,276K: com.android.systemui (pid 3552)
325,780K: com.tencent.mobileqq (pid 1402 / activities)
298,708K: com.tencent.mm (pid 4045 / activities)
269,648K: com.douban.frodo (pid 6511)
245,488K: com.android.phone (pid 3548)
242,148K: com.miui.home (pid 23433 / activities)
231,992K: com.miui.securitycenter.remote (pid 32725)
221,004K: tv.danmaku.bili (pid 5776)
199,776K: com.sohu.inputmethod.sogou (pid 15661)
168,496K: com.tencent.wework (pid 26035 / activities)
141,364K: com.miui.aod (pid 31856)
134,662K: surfaceflinger (pid 1253)
130,180K: com.mi.health:device (pid 5775)
130,156K: com.tencent.mm:push (pid 22102)
121,716K: com.google.android.gms.persistent (pid 13831)
121,482K: com.equationl.starryskywallpaper (pid 26623)
114,552K: com.android.settings (pid 4751)
109,164K: com.douban.frodo:pushservice (pid 6836)
104,212K: com.tencent.mobileqq:MSF (pid 3095)
104,160K: com.google.android.gms (pid 13826)
103,244K: tv.danmaku.bili:download (pid 26771)
102,764K: tv.danmaku.bili:pushservice (pid 26747)
96,304K: com.android.vending (pid 32161)
94,752K: com.miui.personalassistant (pid 16045)
94,608K: com.tencent.wework:wxa_container0 (pid 26374)
93,236K: com.xiaomi.market (pid 16530)
91,472K: com.android.bluetooth (pid 25257)
90,828K: com.xingin.xhs:longlink (pid 2909)
89,672K: com.android.calendar (pid 22772 / activities)
88,108K: com.miui.voiceassist (pid 11476)
85,172K: system:ui (pid 26808)
82,280K: com.miui.cloudservice (pid 6997)
80,744K: com.miui.phrase (pid 6978)
79,200K: com.miui.powerkeeper (pid 8331)
78,188K: com.miui.miwallpaper (pid 23645)
77,948K: com.miui.analytics (pid 8904)
77,816K: com.google.android.gms.unstable (pid 11157)
75,996K: com.miui.touchassistant:float (pid 17509)
73,892K: com.xiaomi.misettings (pid 5333)
73,524K: tv.danmaku.bili:ijkservice (pid 29513)
72,384K: com.miui.systemAdSolution (pid 10661)
72,016K: com.android.mms (pid 27473)
71,964K: com.xiaomi.aiasst.service (pid 28497)
69,500K: com.google.android.wearable.app.cn:background (pid 21870)
69,172K: com.xiaomi.metoknlp (pid 31268)
67,188K: com.google.android.webview:webview_service (pid 6603)
67,048K: com.xiaomi.xmsf (pid 30709)
66,048K: com.miui.yellowpage (pid 3714)
65,820K: com.lbe.security.miui (pid 23573)
65,684K: com.miui.misound (pid 20012)
65,512K: com.miui.mishare.connectivity (pid 3230)
64,484K: com.google.android.gms.persistent (pid 9804)
64,480K: com.xiaomi.bluetooth (pid 14657)
63,724K: com.miui.screenrecorder (pid 3447)
63,148K: com.xiaomi.account (pid 16742)
62,560K: com.xiaomi.gnss.polaris:remote (pid 3996)
62,360K: com.miui.daemon (pid 5664)
//.....................
可是输出的内容太多,反而不好使,所以咱们需求稍微过滤一下需求的内容。
例如获取某个运用的内存信息能够运用 dumpsys meminfo pkg | pid
,其间的 pkg | pid
能够运用运用包名也能够运用 pid,例如获取微信的内存占用:
cas:/ $ dumpsys meminfo com.tencent.mm
Applications Memory Usage (in Kilobytes):
Uptime: 5210015496 Realtime: 7380395810
** MEMINFO in pid 4045 [com.tencent.mm] **
Pss Private Private SwapPss Rss Heap Heap Heap
Total Dirty Clean Dirty Total Size Alloc Free
------ ------ ------ ------ ------ ------ ------ ------
Native Heap 28441 28396 0 148021 29284 202460 184218 18241
Dalvik Heap 66381 66340 4 5605 67456 75619 67427 8192
Dalvik Other 11010 10296 4 1160 12436
Stack 3068 3068 0 8488 3076
Ashmem 65 48 0 0 848
Gfx dev 10672 10672 0 0 10672
Other dev 18 0 16 0 372
.so mmap 9843 2440 5972 12073 24296
.jar mmap 1006 0 8 0 35676
.apk mmap 17376 0 11736 0 23880
.ttf mmap 1615 0 0 0 19096
.dex mmap 6768 32 6632 64 7488
.oat mmap 225 0 0 0 12964
.art mmap 20532 20020 168 6204 30516
Other mmap 596 24 524 0 1072
GL mtrack 384 384 0 0 384
Unknown 6007 6004 0 40216 6272
TOTAL 405838 147724 25064 221831 285788 278079 251645 26433
App Summary
Pss(KB) Rss(KB)
------ ------
Java Heap: 86528 97972
Native Heap: 28396 29284
Code: 26864 124880
Stack: 3068 3076
Graphics: 11056 11056
Private Other: 16876
System: 233050
Unknown: 19520
TOTAL PSS: 405838 TOTAL RSS: 285788 TOTAL SWAP PSS: 221831
Objects
Views: 2955 ViewRootImpl: 1
AppContexts: 12 Activities: 1
Assets: 33 AssetManagers: 0
Local Binders: 298 Proxy Binders: 621
Parcel memory: 410 Parcel count: 326
Death Recipients: 496 OpenSSL Sockets: 0
WebViews: 0
SQL
MEMORY_USED: 642
PAGECACHE_OVERFLOW: 226 MALLOC_SIZE: 46
DATABASES
pgsz dbsz Lookaside(b) cache Dbname
4 96 55 66/85/25 /data/user/0/com.tencent.mm/no_backup/androidx.work.workdb
4 8 0/0/0 (attached) temp
4 96 40 3/16/4 /data/user/0/com.tencent.mm/no_backup/androidx.work.workdb (2)
4 92 46 3703/27/4 /data/user/0/com.tencent.mm/databases/Scheduler.db
4 108 124 27/31/16 /data/user/0/com.tencent.mm/databases/google_app_measurement.db
cas:/ $
能够看到,输出内容少了许多,可是还是不可精简 ,咱们需求的仅仅这个运用占用的总内存而已。
而在上述输出中,咱们能够看到几种不同的内存占用,他们的含义分别如下:
- VSS – Virtual Set Size 虚拟耗用内存(包括同享库占用的内存)
- RSS – Resident Set Size 实践运用物理内存(包括同享库占用的内存)
- PSS – Proportional Set Size 实践运用的物理内存(份额分配同享库占用的内存)
- USS – Unique Set Size 进程独自占用的物理内存(不包括同享库占用的内存)
咱们一般需求检查的是 PSS 值,因而咱们能够将上述指令加上过滤操作:
dumpsys meminfo com.tencent.mm | grep "TOTAL PSS"
回来如下:
cas:/ $ dumpsys meminfo com.tencent.mm | grep "TOTAL PSS"
TOTAL PSS: 404910 TOTAL RSS: 287560 TOTAL SWAP PSS: 219203
cas:/ $
其间在指令后边添加的 grep
表明过滤内容,后边跟着的内容即表明需求查找的内容,支撑正则,也支撑字符串匹配,在这儿的意思是仅过滤出包括 “TOTAL PSS” 的这一行输出。
而两个指令之间的 |
符号是管道衔接符,表明衔接两个指令。
终究,还能够经过以下两种不同的方法获取到当时手机的总内存占用:
dumpsys meminfo | grep -n "RAM"
或 procrank | grep "RAM"
。
从指令不难看出,它们分别是从不同的指令中过滤出咱们需求的总内存信息,至于它们原本的输出是什么,感兴趣的能够自己把参数和过滤去掉履行一下看看。
后边的指令咱们就不一一解释这个过滤操作了。
终究的终究,附加几个检查当时体系的 APP 可用最大内存装备的方法:
- 单个运用的最大内存约束
getprop | grep heapgrowthlimit
- 运用发动后分配的初始内存
getprop|grep dalvik.vm.heapstartsize
- 单个java虚拟机的最大内存约束
getprop|grep dalvik.vm.heapsize
CPU 当时占用信息
获取 CPU 的占用信息仍旧是有两种方法。
方法一:
top -n 5 | grep "com.tencent.mm"
其间 -n 5
表明指定履行 5 次,如果不指定次数则会实时刷新当时的 CPU 占用情况。
回来数据:
cas:/ $ top -n 5 | grep "com.tencent.mm"
7491 shell 20 0 2.0G 3.6M 2.9M S 0.0 0.0 0:00.01 grep com.tencent.mm
4045 u0_a276 20 0 129G 91M 2.1M S 0.6 1.1 10:38.22 com.tencent.mm
需求留意的是,第一行数据不是微信的占用数据啊,这个是咱们方才履行的这个 shell 的占用信息。
第二行才是微信的信息,其间的 “S 0.6” 即微信的占用率,这儿表明为 0.6% 。
第二种方法:
dumpsys cpuinfo | grep "com.tencent.mm"
回来数据:
cas:/ $ dumpsys cpuinfo | grep "com.tencent.mm"
0.6% 4045/com.tencent.mm: 0.4% user + 0.1% kernel / faults: 122 minor 18 major
0.2% 22102/com.tencent.mm:push: 0.1% user + 0% kernel / faults: 47 minor 1 major
cas:/ $
第一列数据即微信的总占用率,后边则是它的详细数据。
CPU 温度
咱们能够经过读取 /sys/class/thermal/thermal_zone*/temp
文件获取到各个传感器的温度值,即:
cat /sys/class/thermal/thermal_zone*/temp
回来数据:
cas:/ $ cat /sys/class/thermal/thermal_zone*/temp
35400
36200
35800
35000
36200
35800
35800
36200
35200
35200
35600
36000
35800
35600
36000
35600
34500
36400
36400
37000
36600
36000
36600
36200
36200
36200
37000
35400
35400
35400
35800
35000
36200
36200
36600
35800
35600
35600
36000
35600
35600
36000
34900
33000
35000
37400
// ...............
能够看到,回来了许多数据,那么哪个才是咱们需求的数据呢?
咱们能够经过读取 /sys/class/thermal/thermal_zone*/type
文件获取到每一行对应的是什么的传感器,即:
cat /sys/class/thermal/thermal_zone*/type
回来数据:
1|cas:/ $ cat /sys/class/thermal/thermal_zone*/type
aoss0-usr
cpu-0-0-usr
cpu-1-3-usr
cpu-1-4-usr
cpu-1-5-usr
cpu-1-6-usr
cpu-1-7-usr
gpuss-0-usr
aoss-1-usr
cwlan-usr
video-usr
ddr-usr
cpu-0-1-usr
q6-hvx-usr
camera-usr
cmpss-usr
npu-usr
gpuss-1-usr
gpuss-max-step
apc-0-max-step
apc-1-max-step
pop-mem-step
cpu-0-0-step
cpu-0-2-usr
cpu-0-1-step
// ........
如果不想要这么多数据,仅仅想要某个方位的数据,那么也不需求用过滤,直接把上面文件路径中的 *
换为相应的序号(即行号)即可,例如获取 cpu-0-0-usr 的温度:
cat /sys/class/thermal/thermal_zone1/temp
电池温度
如果你觉得从上面的一堆数据中找出电池的温度数据太麻烦了,那么你能够运用别的一个单独的指令获取到电池的温度数据:
dumpsys battery | grep temperature
CPU 当时频率
获取 CPU 的当时频率依然是有两种方法。
方法一:
cat /sys/devices/system/cpu/cpuX/cpufreq/scaling_cur_freq
其间的 cpuX 为 cpu 的中心序号,例如想获取序号为 0 的中心的运转频率能够写成:
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq
回来数据:
cas:/ $ cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq
1804800
需求留意的是这种方法回来的不是硬件当时实践的运转频率,而是调度程序发送给硬件,让硬件应该以这个频率来运转,一般来说,此刻硬件确实是以这个频率运转的,可是有时分硬件可能会有点“固执”,就是不按这个频率运转,此刻咱们就需求别的一个指令:
cat /sys/devices/system/cpu/cpuX/cpufreq/cpuinfo_cur_freq
其间的 cpuX 仍旧为 cpu 的中心序号。
现在回来的就是当时硬件实践的运转频率了,可是很不幸,在高版别安卓上现已不允许读取这个文件了,想要读取的话有必要拥有 root 权限。
编写监控脚本
现在,咱们现已知道了获取所需数据的 shell 指令了,是时分来编写一个脚本实时获取需求的数据了:
echo "watcher running..."
log_path="/sdcard/watcher.log"
echo "start watch state at $(date)n" >> $log_path
while true
do
sleep 1
gpu_temp=$(cat /sys/class/thermal/thermal_zone1/temp)
msg="$(date), GPU Temp: $gpu_tempn"
echo "$msg"
echo "$msg" >> $log_path
cpu_temp=$(cat /sys/class/thermal/thermal_zone0/temp)
cpu_0=$(cat /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_cur_freq)
cpu_1=$(cat /sys/devices/system/cpu/cpu1/cpufreq/cpuinfo_cur_freq)
cpu_2=$(cat /sys/devices/system/cpu/cpu2/cpufreq/cpuinfo_cur_freq)
cpu_3=$(cat /sys/devices/system/cpu/cpu3/cpufreq/cpuinfo_cur_freq)
msg="CPU Temp: $cpu_temp, CPU0: $cpu_0, CPU1: $cpu_1, CPU2: $cpu_2, CPU3: $cpu_3, n"
echo "$msg"
echo "$msg" >> $log_path
cpu_usage_info=$(top -n 1 | grep "com.xxxx.yyyy")
msg="CPU Usage: $cpu_usage_info"
echo "$msg"
echo "$msg" >> $log_path
# 这个速度非常慢,如果对记载速度有要求的话,最好不要加这个
mem_info_total=$(procrank | grep "RAM")
mem_info_current=$(dumpsys meminfo com.xxxx.yyyy | grep -n "TOTAL PSS")
msg="Mem Info, Total: $mem_info_total, Current: $mem_info_currentn"
echo "$msg"
echo "$msg" >> $log_path
done
上面这个脚本非常的简略,就是开启一个 while 循环,然后每隔 1s 读取一次各项数据,并将其写入 /sdcard/watcher.log
文件中。
咱们将以上脚本内容保存为恣意文件,例如: watcher.sh
,然后放到设备的恣意方位,例如 /sdcard/watcher.sh
。
在 shell 中履行 sh /sdcard/watcher.sh
即可,运转后咱们只需求正常运用咱们的设备,这个脚本会在后台静静的帮咱们把数据都记载下来的。
PS:上面这个脚本因为仅仅自己运用,所以各项数据挑选做的很粗糙,筛了一堆没用的数据进来,各位大佬运用的时分最好根据自己需求重新挑选一下数据。
结束
在开着这个脚本跑了一天之后,终究发现,随着设备的开机,温度一向在上升,上升到某个值后好像撞到了温度墙,就开端在这个温度范围内波动,同时 CPU 的各个中心运转频率开端大幅的下降。除此之外,其他各项数值均未见异常。
换句话说,这证明了形成卡顿的原因确实是因为散热不可导致 CPU 降频,终究导致体系卡顿。
在这铁一般的依据面前,老板总算“放过了”我,转而去找硬件工程师去了。哈哈哈哈。