目录

目录

1、内存泄露优化

1.1 抓取内存泄露方法

2、启动优化

2.1、冷启动

2.1.1 抓取冷启动方法

2.2、热启动

2.2.1 抓取热启动方法

2.3 启动优化 

3、卡顿优化

3.1、CPU占用查询

3.1.1 通过脚本命令抓取CPU占用

3.1.2 抓取trace文件

3.1.3 CPU分析

3.1.4 通过perfetto工具或者systrace分析trace文件

3.2、内存占用分析

3.2.1 通过脚本命令获取内存

3.2.2 抓取trace文件

3.2.3 内存分析

3.3 卡顿分析

4、性能抓取命令总结

5、抓取CPU和内存脚本命令

1、内存泄露优化

1.1 抓取内存泄露方法

方法一(适用于右发际线高着使用):

1、执行dumpsys meminfo ‘包名’记录当前内存占用

2、执行压测脚本循环遍历业务功能

3、压测一段时间后退出界面等待一段时间后输入命令:dumpsys meminfo ‘包名’查看Activities是否为0,内存有无明显增加的情况

方法二(适用于左发际线高着使用):

通过Android Profile抓取内存快照查看是否有内存泄露

1、使用Memory Profile实时跟踪应用的内存信息,针对内存分配异常的情况,可以通过抓取内存快照进行分析

2、点击Profile的Memory显示条即可进入Memory Profile

 

2、启动优化

2.1、冷启动

2.1.1 抓取冷启动方法

方法一:通过串口命令获取

在测试前先查询进程是否存在 命令 ps -A | grep 包名如果进程存在则需要杀死进程使用 命令 kill -9 进程号在杀死进程后在使用启动命令查看启动时间 命令am start -W 包名/类路径名

应用自身启动耗时,参考TotalTime;

系统启动应用耗时(包含上一个应用activity的pause),参考WaitTime;

方法二:抓取trace判断启动时长

分析冷启动时间:定位到当前应用的UI Thread,如下图,从ZygoteInit到serviceStart后的第一帧结束,查看时间长度做为启动时长(注意该时长比应用测试部用视频采集卡采集的时间要短,视频采集卡采集的是用户感知的真正时长,我们这里的时长仅用作自己应用启动时长分析);

2.2、热启动

2.2.1 抓取热启动方法

方法一:通过命令抓取

让应用退出前台在测试前先查询进程是否存在,如果存在在进行下步操作 命令 ps -A | grep 包名使用启动命令查看启动时间 命令am start -W 包名/类路径名

方法二:抓取trace判断启动时长

 分析热启动时间:定位到当前应用的UI Thread,如果是activity进入,则从activityStart到serviceStart后的第一帧结束,查看时间长度做为启动时长。

如果是service进入,则从serviceCreate到serviceStart后的第一帧结束,查看时间长度做为启动时长。

2.3 启动优化 

启动我们可以查看是否Application初始化或者Activity初始化时存在耗时,可以做出如下改善

ContentProvider:

ContentProvider的onCreate方法会在Application的onCreate方法前执行,其onCreate方法不要放耗时操作,可以通过Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler()延时加载

Application层面:

将Application的onCreate方法中第三方SDK初始化放入线程中处理Multidex预加载优化,5.0以下多dex情况

Activity层面:

首屏Activity的渲染不要有耗时操作,如果有可以放入子线程或者IntentService中处理预创建Activity,对象预创建预加载数据

UI层面:

消除启动时的白屏、黑屏。windowbackground避免布局嵌套,多层嵌套

参考:启动优化

3、卡顿优化

3.1、CPU占用查询

3.1.1 通过脚本命令抓取CPU占用

1、定义脚本文件gen_memory.sh(代码在第五章节),修改包名和U盘挂载路径

2、将gen_memory.sh拷贝到/data/目录,赋权限777(chmod 777)

3、运行脚本(进入到data目录后输入 ./gen_memory.sh),然后遍历各选项功能;

4、退出应用,然后运行脚本(进入到data目录后输入 ./gen_memory.sh);

5、等待一段时间后,建议10分钟以上,拔出U盘,查看U盘中指定包名文件夹下的cpu.csv文件,进行分析查看内存占用是否合理。

3.1.2 通过TOP命令抓取CPU占用

分析系统和进程的CPU占用情况:top -m 20 -n 1 (输出一次cpu占用前20的所有进程数据)

分析高占用进程的线程CPU实时占用情况:top -Hp1635 -m 20 -d 1(输出pid为1853 的进程中cpu占用前20的线程数据,一秒一次)

技巧:同时开两个cmd窗口,分别输入命令同时监控进程和线程的实时占用,可定位到线程,可做成脚本控制频率,动态采集高占用的进程,再结合业务逻辑做归因分析。

3.1.3 抓取trace文件

1、通过脚本命令抓取trace

抓取10秒内trace:

atrace -z -b 10000 gfx input view wm am res dalvik rs sched freq idle disk binder_driver binder_lock -t 10 > /data/trace_output1.atrace

持续抓取:

开始抓取 atrace --async_start -c -b 12800 -z gfx view input webview wm am sm audio video camera hal dalvik res rs bionic pm ss database network disk sched freq idle binder_lock binder_driver

复现到后停止 atrace --async_stop -c -b 12800 -z -o /data/local/tmp/trace.out

2、通过profile抓取trace

3.1.3 CPU分析

CPU Profile可以跟踪如下几种数据:

Sample Java Method 为间断采样,适用于大部分情况,profile调试不会对现有程序造成影响Trace Java Method 持续采集,适用于跟踪全部method执行状况,会对现有程序运行造成影响Sample C 跟踪C层函数(Android 8.0+)Trace System Call 跟踪系统调用,如核心运行状态和掉帧情况( Android 7.0+)

3.1.4 通过perfetto工具或者systrace分析trace文件

systrace是google 提供的一个分析工具,脚本命令为Android SDK的platform-tools\systrace\systrace.py , 它的运行依赖python2.7+,所以在进行trace分析前需要安装python对应环境。

转换命令:python "D:\Program Files\Android\sdk\platform-tools\systrace\systrace.py" --from-file=D:\9615_trace\trace008.out 

我们可以从frame看出是那一帧卡顿,红色是验证卡顿、橙色为轻微卡顿、绿色不卡顿。然后再从堆栈分析draw、layout的耗时情况。

 线程状态

绿色:正在运行,线程正在完成与某个进程相关的工作或正在响应中断。

蓝色:可运行,线程可以运行但目前未进行调度。

白色:休眠,线程没有可执行的任务,可能是因为线程在遇到斥锁定时被阻止。

橙色:不可中断的休眠,线程在遇到 I/O 操作时被阻止或正在等待磁盘操作完成。

紫色:可中断的休眠,线程在遇到另一项内核操作(通常是内存管理)时被阻止。

3.2、内存占用分析

3.2.1 通过脚本命令获取内存

1、定义脚本文件gen_cpu.sh(代码在第五章节),修改包名和U盘挂载路径

2、将gen_cpu.sh拷贝到/data/目录,赋权限777

3、运行脚本(进入到data目录后输入 ./gen_cpu.sh),然后遍历各选项功能;

4、退出应用,然后运行脚本(进入到data目录后输入 ./gen_cpu.sh);

5、等待一段时间后,建议10分钟以上,拔出U盘,查看U盘中指定包名文件夹下的meminfo.csv文件,进行分析查看内存占用是否合理。

3.2.2 通过TOP命令抓取内存

技巧:1,先使用命令dumpsys meminfo查看设备的内存占用情况,分析内存不足时各个进程的占用,这个命令统计时间较长,比较耗资源,不适合频繁使用

2,使用命令dumpsysmeminfopkg/pid查看对应进程的详细内存分布,用于前期初步分析定位内存的分布:

Native Heap 较高时,一般是Bitmap对象较多(结合Views判断);或者是JNI里C/C++申请的对象占用内存较多Java Heap 较高时大部分占用都是应用程序里的申请的对象内存,抓取内存快照可详细分析Code 较高代表so库,jar包数量多;代码量大,大量的类加载(多个class.dex分包),资源多等原因,可解压apk查看,需要关注apk瘦身优化Stack 占用高通常是线程数量多太,栈内存高导致,检查无用线程,尽量用线程池创建线程Graphics 是UI渲染任务繁重,检查Views层级,过度绘制,注重图层优化System 是可系统相关的内存,可能是使用较多的系统服务,应用程序框架等原因通过Activites数量持续增加/退出后不减少的情况,可以初步判断内存泄漏

3.2.2 抓取trace文件

1、通过脚本命令抓取trace

抓取10秒内trace:

atrace -z -b 10000 gfx input view wm am res dalvik rs sched freq idle disk binder_driver binder_lock -t 10 > /data/trace_output1.atrace

持续抓取:

开始抓取 atrace --async_start -c -b 12800 -z gfx view input webview wm am sm audio video camera hal dalvik res rs bionic pm ss database network disk sched freq idle binder_lock binder_driver

复现到后停止 atrace --async_stop -c -b 12800 -z -o /data/local/tmp/trace.out

2、通过profile抓取trace

3.2.3 内存分析

内存分类:

Java表示Java代码或Kotlin代码分配的内存;Native表示C或C++代码分配的内存(即使App没有native层,调用framework代码时,也有可能触发分配native内存);Graphics表示图像相关缓存队列占用的内存;Stack表示native和java占用的栈内存;Code表示代码、资源文件、库文件等占用的内存;Others表示无法明确分类的内存;

3.3 卡顿分析

Systrace会统计卡顿的具体类型,一般常用的类型有:

1、CPU分配不足:

进程大部分处于Runnable,CPU资源分配不足

Scheduling delay:调度延迟Expensive measure/layout pass:绘测或者布局时间长Long View#draw: view 绘制时间长 

导致卡顿可能有如下原因:

过于复杂的布局UI线程的复杂运算频繁的GC,如内存抖动,即大量的对象被创建后又短时间被回收,或者瞬间产生大量的对象会严重占用内存区域

4、性能抓取命令总结

方式命令帧率 dumpsys gfxinfo 包名 丢帧率:Janky frames的前后差值 / Total frames的前后差值平均帧率= 60-(60*丢帧率) 内存泄露、内存占用 dumpsys meminfo 包名查看前20个进程的cpu使用top -m 20应用安装大小du -sh  安装路径

5、抓取CPU和内存脚本命令

#!/system/bin/sh

#设置包名

packagename="";

#设置U盘路径

path="/mnt/media_rw/2DD0-A606/$packagename";

#设置du与dumpsys的间隔时间

sleeptime=15

#设置top间隔时间,建议15秒以上

toptime=15

a=0

b=0

c=0

d=0

#设置总次数

total=100000

#设置常量

u=1024

#v=6

if [ ! -d "$path" ];

then

mkdir $path

else

rm -rf $path

mkdir $path

fi ;

#查看Android版本

v=$(getprop|grep ro.build.version.release |awk '{print$2}'|awk '{print substr($1,2,1)}');

echo $v >> $path/android_ver.csv ;

#获取CPU个数

processor=$(($(cat /sys/devices/system/cpu/possible |awk -F'[-]' '{print$2}')+1));

#生成ddr表的表头

echo -n "time," >> $path/meminfo.csv;

echo -n "meminfo," >> $path/meminfo.csv;

echo "Activity" >> $path/meminfo.csv;

#生成cpu表的表头

echo -n "time," >> $path/cpu.csv;

echo -n "cpu," >> $path/cpu.csv;

echo "Activity" >> $path/cpu.csv;

while (($b < $total));do

let "b += 1";

if [ -d "$path" ]; then

echo -n $(date "+%H:%M:%S,") >>$path/meminfo.csv;

#echo -n $(date "+%H:%M:%S,") >>$path/ddr.csv;

#输出Meminfo大小 单位:M

ddr=$(dumpsys meminfo $packagename |grep TOTAL |awk 'BEGIN{ddr=0}{ddr += $2}END{print ddr}');

echo -n "`awk -v ddr=$ddr -v u=$u 'BEGIN{printf "%.2f",ddr/u}'`," >>$path/meminfo.csv;

#获取当前Activity

echo $(dumpsys window |grep mCurrentFocus |awk '{print$3}'|awk -F "}" '{print$1}') >>$path/meminfo.csv;

sleep $sleeptime;

fi ;

done&

while (($c < $total));do

let "c += 1";

if [ -d "$path" ];then

#CPU

cpu=$(top -b -n 1|grep $packagename|awk 'BEGIN{cpu=0}{cpu += $9} END {print cpu"%"}');

echo -n $(date "+%H:%M:%S,") >>$path/cpu.csv;

#生成CPU占用

echo -n "`awk -v cpu=$cpu -v processor=$processor 'BEGIN{printf "%.2f%%",cpu/processor}'`," >>$path/cpu.csv;

#获取当前Activity

echo "`dumpsys window |grep mCurrentFocus |awk '{print$3}'|awk -F "}" '{print$1}'`," >>$path/cpu.csv;

sleep $toptime;

fi ;

done&

推荐链接

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: