APP 内存测试--Android Memory Profiler实操(入门版)
背景: 内存作为程序运行的核心资源,合理分配与回收至关重要。不合理的内存管理不仅会导致应用卡顿、ANR或黑屏等问题,严重时甚至引发OOM(内存溢出)崩溃,严重影响用户体验。APP内存测试我们一般关注内存抖动和内存泄漏方面的问题
一、APP内存测试
1.1 内存抖动
内存抖动的产生原因是短时间内有大量的内存申请和释放,造成了内存抖动厉害。内存抖动最直接的影响是可能会造成app使用过程中的卡顿,因为内存抖动伴随着大量的GC,现在的机制GC是会造成短暂的停顿。
1.2 内存泄露
Android系统为每个运行程序设定了内存上限,一旦超出限制便会触发OOM(内存溢出)机制。这通常表现为应用闪退或崩溃。常见诱因包括内存泄漏,以及无节制地占用大量系统资源等不当编程行为。
Android内存在分配之后,由于被生命周期比它长的对象引用造成在使用完之后也无法释放。
1.3 Android Profiler介绍
Android Profiler的Memory Profiler组件, 可用于帮助我们分析内存泄露和内存抖动的问题。Memory Profiler的功能包括: 展示应用内存使用情况的实时图像、抓取内存的dump信息、强制垃圾回收及追踪内存分配。
二、环境准备
2.1 将手机通过USB链接至电脑,连接时需要确认手机是处于“USB调试”模式
2.2 打开AndroidStudio,从底部的控制台选择Android Profiler
如果底部没有Android Profiler选项,可以从顶部工具栏 View—Tool Windows—Profiler
3、在Android Profiler中,点击SESSIONS后方的“+”,然后选择已连接的设备及自己要测试的APP的进程名,如【com.xxxx.xxx】。
4、点击后如下图,我们可以看到Android Profiler分为四大模块: CPU、内存 、网络、能耗:
5、单击MEMORY时间轴中的任意位置打开MEMORY Profiler,如下图所示;点击旁边箭头可返回上一级页面
三、通用测试步骤
1. 进入某一个页面,滑动页面,多滑几次,然后退出页面,进行GC。
2.进入某一页面,操作一些界面元素,比如加载更多元素、删除某个元素、增加某个元素等。
3. 进入某一个页面,接收和处理大量网络数据的场景,比如下载大量的图片等。
4.对于新增模块,比较和上个版本的内存值,内存增加是否符合预期。
5.APP启动过程中(热启动、冷启动、首次启动),内存的变化情况。
Memory Profiler界面按钮说明: 如下图
标注①按键:用于强制内存回收。
标注②按键:用于抓取进程内存的dump信息。
标注③按键:用于记录内存的分配信息。 初次点击Record native allocations时,对应统计的开始时间点;再次点击时,对应统计的结束时间点。进程在两个时间点之间的内存分配信息,将被Memory Profiler记录和分析。
标注④区域:用于缩放/复原时间轴。
标注⑤按键:用于显示/停止显示实时的内存数据。
标注⑥区域:用于记录事件发生的时间点及大致持续的时间(例如点击事件、返回事件、屏幕旋转事件等事件)。
Memory Profiler统计内存种类及其含义:
Total:表示app独自占用内存,后面几项之和;
Java:表示Java代码或Kotlin代码分配的内存;
Native:表示C或C++代码分配的内存(即使App没有native层,调用framework代码时,也有可能触发分配native内存);
Graphics:表示图像相关缓存队列占用的内存;
Stack:表示native和java占用的栈内存;
Code:表示代码、资源文件、库文件等占用的内存;
Others:表示无法明确分类的内存;
Allocated:表示Java或Kotlin分配对象的数量。
四、基本用法
4.1 查看内存分配情况
Memory Profiler可以查看两个时间点之间的内存分配情况
4.1.1 点击内存统计任意区域,会显示两条线,拖拽任意一条线,即可以选择观察区域;
4.1.2 选定观察区域后, 下方Table区域就可以统计这段时间内分配对象的类名;
4.1.3 因为类比较多,我们可以在搜索框,输入关键字(比如:cert),找到我们想要关注的类(可以与开发沟通)后,点击该类,然后会在右侧Instance View显示具体的对象;
4.1.4 点击具体对象后,会在Allocation Call Stack区域显示调用栈。点击调用栈信息后,就会跳转到具体的代码(前提是你本地有代码)。
Allocations :表示实例数量
Shallow Size:表示对象使用Java内存的大小,单位为byte;
4.2 查看内存占用情况
4.2.1 在Memory Profiler界面点击Dump Java heap按钮,抓取点击后一段时间内app占用内存的dump信息(文件可能会比较大),生成堆转储文件 hprof。
4.2.2 抓取后会自动解析hprof文件信息,并且统计出Classes、Leaks、Count、Native Size、Shallow Size、Retained Size数量;
同样我们也可以通过搜索框输入关键字,搜索我们想要关注的类(如:cert),这里我们可以看到有一处泄漏的地方;
4.2.3 点击类后,会在下方Instance显示具体的对象;
4.2.4 点击具体的对象后,会在右侧显示详细信息;
Depth:表示当前对象到任一GC root的最短跳数;
Native Size:表示类对象所引用的Native对象所消耗的内存的大小;
Shallow Size:表示对象使用Java内存的大小,单位为byte;
Retained Size:表示对象占用的实际内存大小,大于等于Shallow Size;
五、测试结果分析
5.1 内存抖动
如果在很短的时间内发生了多次的内存申请和释放,有多处高峰值,虽然也有回收操作,但是尖峰值过多,垃圾回收站频繁操作,导致短时间内执行了多次GC操作。出现这种现象的时候,明显能感觉到App的卡顿,甚至出现应用进程被干掉,这种现象则可判断为此操作中存在内存抖动现象。
5.2 内存泄漏
内存泄露时 Memory Profiler 会呈现一个类似阶梯型的内存上升趋势
六、测试标准
6.1 内存抖动
10s内出现 大于等于 4次的内存波动(图形上有4个毛刺),如下图即为不通过。
6.2 内存泄露
理论上不能有任何的内存泄露。
七、dumpsys meminfo
命令行方式查看内存情况
查看设备内存使用情况:adb shell cat /proc/meminfo
7.1 App运行内存
我们目前主要关注两方面的运行内存:app启动内存平均值、app运行主要功能之后的内存平均值
查看某APP应用内存使用信息:adb shell dumpsys meminfo com.xxxx.xxx(待测应用包名)
属性说明1:
Uptime:表示启动到现在的时长,不包含休眠的时间,单位毫秒(ms)
Native Heap:由C/C++申请的内存空间在native heap
Dalvik Heap:由java对象申请的对象,全部都在Dalvik Heap,我们通常关注一个app的内存是Dalvik Heap。
Dalvik Other:类数据结构和索引占据的内存
Stack:栈内存
Unknown:无法归类到上面提到的内存类型中,除此之外的内存。
TOTAL:各个项占用的内存的总值。
Pss total(Proportional set Size 按比例分配占用内存):实际使用内存
Private Dirty & Clean:指的是进程独占内存。通常我们更关注Private dirty内存,因为这部分内存是在应用退出之后,可以被回收的内存。换句话说,这部分内存是应用 new 出来的对象实例。
SwapPss Dirty:用于监视系统中内存交换操作的情况
Heap size:虚拟机Heap 的大小,指占用总内存(Heap 堆)
Heap Alloc : 统计的是虚拟机分配的所有应用实的内存大小
Heap Free: 虚拟机当前可用的Heap大小(空闲内存)。
属性说明2:
说明:
Graphics:为图形的内存使用情况
Total PSS:上下两部分其实是对此应用使用内存量做了不同分类,值是相同的,都是75685
Objects:表示该app的一些对象的数量,例如view控件存在44个,Activity分配的内存有1个
SQL & DATABASES:是app操作过程中数据库相关的内存情况
Asset Allocations : 这里是将asset下面文件解压之后,放到内存中。
7.2 结果分析
7.2.1 建议App占用内存采用 Pss Total 的值。(因为这个是app 进程真实占用的内存值)。
至于Heap Size、Heap Alloc、Heap Free是Dalvik所分配的堆大小(反映了java代码分配的内存,但是也会包含Zygote共享的内存)
7.2.2 建议采集3到5次的内存数据,求平均值。
7.2.3 对前后两个版本的内存(或者三个版本)进行数据对比。
八、 其他说明
Android系统对dalvik的vm heapsize作了硬性限制,当java进程申请的java空间超过阈值时,就会抛出OOM异常(这个阈值视机型而定)
查看单个应用最大内存限制,我们可以在shell中输入命令:getprop|grep heapgrowthlimit
可以看到我的测试机(荣耀Magic3)的限制为384M,dalvik process 超过就会抛OOM(out of memory)异常