移动性能测试 Android 性能测试实践 (一)

testly · 2015年05月17日 · 最后由 airFloat 回复于 2020年07月31日 · 9143 次阅读
本帖已被设为精华帖!

前言:

前段时间花了两周的时间做了一个基于 Android 客户端的性能测试现在分享给大家,希望对大家有所帮助!

Look-Look

1,既然是基于 Android 客户端的性能测试那就与后台的一些 API、数据接口要区分开来~!
2,Android 的性能测试能其实包括很多很多的测试项比如:资源消耗,内存泄露,电量功耗,启动耗时,渲染等等....

怎么去做?
1,采集数据 :采集的数据包括:内存、cpu、电量功耗、hprof(内存泄露分析文件)、响应时间等等。。。。
2,设计场景 :搞定数据的采集后配合一些固定的场景来收集一些数据(最好多取几次而且每次配合不同的设备看平均值)作为最后的对比分析
3,结果分析 :拿到数据后分析哪些模块的数据异常再去 Check code 定位问题的原因

好吧下面慢慢跟大家详细的说吧!

先写下内存篇

内存的采集:

Android 的内存的采集这边介绍三种方式:

1,通过 Dumpsys 来取值

adb shell dumpsys meminfo 

这里可以看到当前所有进程的内存信息!

如果你要看详细的内存:

adb shell  dumpsys  meminfo  pakagename or Pid

看其中的 Size 可以发现 Native Heap 和 Dalvik Heap 占据了 Heap Size
dalvik 就是我们平常说的 java 堆,我们创建的对象是在这里面分配的。
对于内存的限制 这里纠正一下:是 dalvik heap 不能超过最大限制,跟 Native heap 没有关系!
最大限制查看:

#查看单个应用程序最大内存限制
 adb shell getprop|grep heapgrowthlimit

得到结果:

|[dalvik.vm.heapgrowthlimit]: [96m]

这个 96M 是单个程序限制最大内存,而 meminfo 里面的 dalvik heap size 的最大值若果超出了 96m 那就很可能会发生 OOM
dalvik.vm.heapgrowthlimit 和 dalvik.vm.heapsize 都是 java 虚拟机的最大内存限制,应用如果不想在 dalvik heap 达到 heapgrowthlimit 限制的时候出现 OOM,需要在 Manifest 中的 application 标签中声明 android:largeHeap=“true”,声明后应用 dalvik heap 达到 heapsize 的时候才会出现 OOM!

注:设备的不一样 最大内存限制也可能不一样

现在大多数手机 的 android 程序内存一般限制在 96M 以上甚至更高,也可能更低。

3,用/system/xbin/procrank 工具 来取值很直观

adb shell procrank

VSS – Virtual Set Size 虚拟耗用内存(包含共享库占用的内存)
RSS – Resident Set Size 实际使用物理内存(包含共享库占用的内存)
PSS – Proportional Set Size 实际使用的物理内存(比例分配共享库占用的内存)
USS – Unique Set Size 进程独自占用的物理内存(不包含共享库占用的内存)

USS 是针对某个进程开始有可疑内存泄露的情况, 是一个程序启动了会产生的虚拟内存,一旦这个程序进程杀掉就会释放!

3,使用 ActivityManager 的 getMemoryInfo(ActivityManager.MemoryInfo outInfo)(这个方法是写一个简单的 app 去监控的时候用到的,轻便简单)

private void GetMemory() {    

    final ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);    

    ActivityManager.MemoryInfo info = new ActivityManager.MemoryInfo();   

    activityManager.getMemoryInfo(info);    

    Log.i(tag,"系统剩余内存:"+(info.availMem >> 10)+"k");   

    Log.i(tag,"系统是否处于低内存运行:"+info.lowMemory);

    Log.i(tag,"当系统剩余内存低于"+info.threshold+"时就看成低内存运行");

}


availMem:表示系统剩余内存

lowMemory:它是 boolean 值,表示系统是否处于低内存运行

hreshold:它表示当系统剩余内存低于好多时就看成低内存运行

我用过以上三种最多,其实 Top 也可以 还有很多方法都可以。

adb shell top  

内存拿到后怎么去用呢?

这里我用的方法是用 java 封装 Adb shell dumpsys meminfo 再用字符串截取 打印的方式


public static String GetMemory(String packageName) throws IOException, InterruptedException {

    String str3=null;
      Runtime runtime = Runtime.getRuntime();
      Process proc = runtime.exec("adb shell dumpsys meminfo "+packageName);
      try {

          if (proc.waitFor() != 0) {
              System.err.println("exit value = " + proc.exitValue());
          }
          BufferedReader in = new BufferedReader(new InputStreamReader(
                  proc.getInputStream()));
          StringBuffer stringBuffer = new StringBuffer();
          String line = null;
          while ((line = in.readLine()) != null) {
              stringBuffer.append(line+" ");

          }
          String str1=stringBuffer.toString();
          String str2=str1.substring(str1.indexOf("Objects")-60,str1.indexOf("Objects"));     
          str3=str2.substring(0,10);
          str3.trim();
      } catch (InterruptedException e) {
          System.err.println(e);
      }finally{
          try {
              proc.destroy();
          } catch (Exception e2) {
          }
      }
    return str3 ;
}

}

截取好之后呢 可以跟 其他的一些系统资源值拼在一起打印出来:

拿到这些值之后可以配合手工或自动化来做数据收集,你会看到有些步骤内存占用很高或者 Cpu 消耗也会较高,这样你就可以去 check 一下 关于这个步骤相关 的 Code

未完待续

欢迎一起交流,一起进步 可以关注我的微信公众号:“测试开发进阶” - 点我关注

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 70 条回复 时间 点赞

:thumbsup: 收藏了。

我纠正一些说法。

关于单个应用占用量的问题。应该先通过如下的手段进行查看。

#查看单个应用程序最大内存限制
 adb shell getprop|grep heapgrowthlimit
|[dalvik.vm.heapgrowthlimit]: [96m]

#应用启动后分配的初始内存
 adb shell getprop|grep dalvik.vm.heapstartsize
|[dalvik.vm.heapstartsize]: [8m]

#单个java虚拟机最大的内存限制
 adb shell getprop|grep dalvik.vm.heapsize
|[dalvik.vm.heapsize]: [384m]

关于四个内存占用量

USS – Unique Set Size 进程独自占用的物理内存(不包含共享库占用的内存)

USS 是针对某个进程开始有可疑内存泄露的情况, 是一个程序启动了会产生的虚拟内存,一旦这个程序进程杀掉就会释放!
没错。不过 USS 需要通过 root 的手机。一般没有 root 的手机我们可以获取 PSS。而 PSS 的话可以通过如下命令来获取

adb shell dumpsys meminfo <Package Name>|grep TOTAL

#2 楼 @monkey 补充得到位

感谢分享

#4 楼 @doctorq 卧槽,你这么客气干甚呢!干嘛呢!

@testly
看了大神写的东东收获颇多啊,但是小白的我有几个问题,
1.native+dalvik 不能超过最大限制。 ---> 这个值的相加从图片上看,具体值的是什么值呢?是 pss?
2.你说的 “android 程序内存一般限制在 16M,当然也有 24M 的” @monkey 中说的 adb shell getprop|grep heapgrowthlimit 这个命令查看呢? 如果是,你说的内存限制应该是在模拟器上的吧? 我用我的手机查看时 256M,如下图:

  1. Native 和 dalvik 对应的 heap size ,我的理解是 这个应用程序总共需要的 heap size 大小,而 Heap alloc 是我 cpu 分配给你的内存使用,后面的 free 是 heap size - alloc 的值,是这样理解的?

@monkey

使用了下,您提供的命令,发现第二个,我试着没有反馈的值,是不是这个命令有前提条件的哦~~

感谢分享啊,我一直有个疑问哦,请教老师,请问 CPU 占用问题,假如多核 Cpu,如何去计算某个 app 的 cpu 占用呢?我感觉需要在一段时间内,计算各个 cpu 被 app 占用情况,然后再做平均的算法。

#5 楼 @testly 应该感谢每一个花时间分享的同学。

testly #10 · 2015年05月18日 Author

#6 楼 @felixtest
1,
你看下面的 native 和 dalvik 的 total,当总数也就是 total 这一列超过单个程序内存的最大限制时,OOM 就很有可能会出现了

2,
Naitve Heap Size: 从 mallinfo usmblks 获得,代表最大总共分配空间

Native Heap Alloc: 从 mallinfo uorblks 获得,总共分配空间

Native Heap Free: 从 mallinfo fordblks 获得,代表总共剩余空间

testly #11 · 2015年05月18日 Author

#9 楼 @doctorq 那首先我也感谢你分享的那些让我受益匪浅的东西

#11 楼 @testly 有时间沟通沟通一下这些东西

testly #13 · 2015年05月18日 Author

#8 楼 @cpfeng0124 adb shell dumpsys cpuinfo 可以看到你的应用 Cpu 占比,和内核 Cpu 占比 前面是的是 total

testly #14 · 2015年05月18日 Author

#12 楼 @doctorq 随时找我!

#14 楼 @testly @doctorq 你们不要在贴子里搞基!

评论亮了。但是帖子内容还是杠杠滴……收藏了

#7 楼 @felixtest hi ~这个一般我不怀疑是不是 windows 和 linux 的区别,但是这个数据会和 OS 有很大关系,因为是读取 shell 文件的配置的。所以你可以去找下这个文件。文件的路径/system/build.prop

18楼 已删除

@testly

1、你说的这个 Total 的总和 是指 Heap Size 值的和 与 256M 相比吧,如果超过 256 就会 OOM,如下图:

2、对应一下 3 个的内存分配,具体有啥区别?不是很理解,我的理解如下:

Naitve Heap Size: 从 mallinfo usmblks 获得,代表最大总共分配空间 ----> 是指这个 app 当前最大分配到的空间

Native Heap Alloc: 从 mallinfo uorblks 获得,总共分配空间 ------> 这个是指,这个 app 中需要运行时,分配的内存空间,也就是由 usmbiks 给予的

Native Heap Free: 从 mallinfo fordblks 获得,代表总共剩余空间 ---> 最大的空间 - 总共分配空间 ,余下的空闲空间

是我这样理解的吗?

testly #20 · 2015年05月18日 Author

#19 楼 @felixtest 哦了~!

  • MemTotal: 所有可用 RAM 大小。
  • MemFree: LowFree 与 HighFree 的总和,被系统留着未使用的内存。
  • Buffers: 用来给文件做缓冲大小。
  • Cached: 被高速缓冲存储器(cache memory)用的内存的大小(等于 diskcache minus SwapCache)。
  • SwapCached:被高速缓冲存储器(cache memory)用的交换空间的大小。已经被交换出来的内存,仍然被存放在 swapfile 中,用来在需要的时候很快的被替换而不需要再次打开 I/O 端口。
  • Active: 在活跃使用中的缓冲或高速缓冲存储器页面文件的大小,除非非常必要,否则不会被移作他用。
  • Inactive: 在不经常使用中的缓冲或高速缓冲存储器页面文件的大小,可能被用于其他途径。
  • SwapTotal: 交换空间的总大小。
  • SwapFree: 未被使用交换空间的大小。
  • Dirty: 等待被写回到磁盘的内存大小。
  • Writeback: 正在被写回到磁盘的内存大小。
  • AnonPages:未映射页的内存大小。
  • Mapped: 设备和文件等映射的大小。
  • Slab: 内核数据结构缓存的大小,可以减少申请和释放内存带来的消耗。
  • SReclaimable:可收回 Slab 的大小。
  • SUnreclaim:不可收回 Slab 的大小(SUnreclaim+SReclaimable=Slab)。
  • PageTables:管理内存分页页面的索引表的大小。
  • NFS_Unstable:不稳定页表的大小。

搬运了下 http://blog.csdn.net/sxwyf248/article/details/5981251

对于内存的限制是 native+dalvik 不能超过最大限制。
android 程序内存一般限制在 16M,当然也有 24M 的。

这段有问题,

# 查看单个应用程序最大内存限制

 adb shell getprop|grep heapgrowthlimit
|[dalvik.vm.heapgrowthlimit]: [96m]

都说了 dalvik 了, 和 native 有个毛关系? @testly

不收藏对不起自己

果断收藏,还没来得及消化

@testly 你还没回答我问题呢

testly #27 · 2015年05月19日 Author

#26 楼 @lihuazhang 卧槽 我还没看到你发了问题!

现在回答你:
在 Android Native Code 中使用 malloc 分配出来的内存,就是 Native Heap. 这部分内存不受 Java Object Heap 限制,可以自由使用,但是会受到系统限制。

dalvik 是指 dalvik 所使用的内存

native:是被 native 堆使用的内存。应该指使用 C\C++ 在堆上分配的内存。

这里的 dalvik 使用的内存 +native 使用的内存 大于 程序限制最大的内存 就会发生 OOM

#27 楼 @testly dalvik 使用的内存 +native 使用的内存 大于 程序限制最大的内存 我的问题是不应该吧 native 算在里面,而是单 dalvik 才能促发 oom

testly #29 · 2015年05月19日 Author

#28 楼 @lihuazhang 要算在里面的哦 !
android 程序内存被分为 2 部分:
native 和 dalvik,dalvik 就是我们平常说的 java 堆,我们创建的对象是在这里面分配的,而 bitmap 是直接在 native 上分配的,对于内存的限制是 native+dalvik 不能超过最大限制。

#29 楼 @testly adb shell getprop|grep heapgrowthlimit
|[dalvik.vm.heapgrowthlimit]: [96m]

这个最大限制值得是 dalvik 的吧?

testly #31 · 2015年05月19日 Author

#30 楼 @lihuazhang 这 dalvik .vm 是单个 java 虚拟机的 heapgrowthlimit ,你可以理解为 一个程序启动就是启动一个 java 的虚拟机而这个虚拟器的最大内存就是 96M

#31 楼 @testly C/C++ 申请的内存空间在 native heap 中,而 java 申请的内存空间则在 dalvik heap 中。
这个是因为 Android 系统对 dalvik 的 vm heapsize 作了硬性限制,当 java 进程申请的 java 空间超过阈值时,就会抛出 OOM 异常(这个阈值可以是 48M、24M、16M 等,视机型而定),可以通过 adb shell getprop | grep dalvik.vm.heapgrowthlimit 查看此值。

也就是说,程序发生 OMM 并不表示 RAM 不足,而是因为程序申请的 java heap 对象超过了 dalvik vm heapgrowthlimit。也就是说,在 RAM 充足的情况下,也可能发生 OOM。

这样的设计似乎有些不合理,但是 Google 为什么这样做呢?这样设计的目的是为了让 Android 系统能同时让比较多的进程常驻内存,这样程序启动时就不用每次都重新加载到内存,能够给用户更快的响应。迫使每个应用程序使用较小的内存,移动设备非常有限的 RAM 就能使比较多的 app 常驻其中。但是有一些大型应用程序是无法忍受 vm heapgrowthlimit 的限制的

testly #33 · 2015年05月19日 Author

#32 楼 @lihuazhang 恩恩,是这样的,OOM 跟 RAM 没有直接关系的

#33 楼 @testly 额。。。这个就是 dalvik.vm.heapgrowthlimit 的单个应用的内存上限。。。单个应用程序最大内存限制,超过这个值会产生 OOM

testly #35 · 2015年05月19日 Author

#34 楼 @monkey 是这样的!

#35 楼 @testly
请问这个是怎么生成的?testng?

testly #37 · 2015年05月20日 Author

#36 楼 @hoohyou 这是 ReportNG 只是类似 System.out 的东西放到这个网页上而已,

实际上 dalvik.vm.heapgrowthlimit 和 dalvik.vm.heapsize 都是 java 虚拟机的最大内存限制,应用如果不想在 dalvik heap 达到 heapgrowthlimit 限制的时候出现 OOM,需要在 Manifest 中的 application 标签中声明 android:largeHeap=“true”,声明后应用 dalvik heap 达到 heapsize 的时候才会出现 OOM

testly #39 · 2015年05月22日 Author

#38 楼 @raowm520 我试了一下 的确是这样的,最大内存限制只是限 Dalvik heap 的大小 ,我纠正一下! @lihuazhang

#39 楼 @testly 好,主贴也改下~

收藏了


CPU 为什么有大于 100% 的

testly #43 · 2015年08月07日 Author

#42 楼 @75281920 我拿的 meminfo 里面的 vss ,而且大于 100 很正常现在手机一般都多核芯!

#43 楼 @testly adb shell top 和 adb dumpsys cpuinfo 有什么区别吗?还是没怎么懂,top 就没有超过 100 的情况

收藏,等待大爷后续

proc.waitFor 这个方法会一致阻塞住,另外,代码中执行 “adb shell dumpsys cpuinfo XX" 会提示找不到 daemon not running. starting it now on port 5038” 错误,应该是不需要 adb shell 操作,但是会遇到权限问题,不知道楼主是怎么解决的啊

testly #48 · 2015年12月11日 Author

#47 楼 @alfor 你线程开启之后记得结束退出,还有后面的问题应该是端口被占用了吧~


为什么出现这种情况叻

匿名 #50 · 2016年08月18日

@testly 为什么 pss total 会小于 heap alloc?

testly #51 · 2016年08月18日 Author

#49 楼 @liyaoyao procrank 有些手机需要自己安装进去

@testly 您好我一直有个疑惑,内存使用量不断地增长大一个值后趋于平稳,但是没有超过可以使用的最大内存,这种情况是有 oom 的可能吗

testly #54 · 2016年10月13日 Author

#53 楼 @malina 你说这个问题需要关注一下持续增长过程 dalvik heap size 和 GC 的情况, 另外还有整体内存抖动的频率。
因为在不同的场景也可能在某一个场景下导致 dalvik heap 飙到阀值。

我的电脑是 Mac,连上了手机,为什么输入 adb shell getprop|grep heapgrowthlimit 没有结果出来

不知道楼主还关注这个帖吗,我想问一下,如果按你的方法我看都要超出最大限制很多,还说 size 的数值要先换算吗

超出了几万,是不是 heap size 的数值要先做什么换算然后再对比呢

testly #57 · 2016年11月14日 Author

#56 楼 @enumerate 上面单位是 KB 你可以/1024

#57 楼 @testly 多谢楼主,已经查到了,忘了这里说一声

testly #59 · 2016年11月14日 Author

#58 楼 @enumerate 是我回答晚了😺

#59 楼 @testly 😋 看了楼主的工具,我也准备自己写点什么,等写完了,帮忙指点啊

testly #61 · 2016年11月14日 Author

#60 楼 @enumerate 没问题
,加油,写完发帖分享一下

#61 楼 @testly 要让楼主失望了,本来也想像你一样写一个实时监控的工具,但是我忽然想起,你用的 java,我用的 python,我还不知道 python 有没有可以写成那样的库,我要转变一下思路了。。

匿名 #63 · 2016年11月15日

#56 楼 @enumerate adb shell dumpsys meminfo + packgagename 的出来的 PSS Total 单位是 kB
adb shell getprop | grep heapgrowthlimit 得到的结果是 192M,肯定是要进行单位转换后才能对比的。

#63 楼 @lanlanxia 好的,已经知道了不过还是多谢,我的 adb 命令出来后,没有那个单位 kb 的提示,好像是我 adb 的版本的问题

赞一个

楼主不好意思,又来叨扰了,我现在也要写一个和你一样的程序,但我用的是 python+appium,但我现在的思路是每做一个操作,就获取一下内存,比如我点击发送按钮,这之后就 os.system("adb shell dumpsys meminfo xxxx"),但是我这里有个错误

应该是,appium 开启时就启动了 adb,结果我用例里又用了 adb 命令导致的吧。请问你这里获取数据的时候,是否也是每座一个操作就获取一下内存和 cpu 的值,还是说等整体用例执行完在获取内存和 cpu 的值呢

testly #67 · 2017年03月09日 Author
enumerate 回复

你好,建议把数据打点做成异步处理,然后与 App 自动化进程进行同步通讯,来进行 记录~

坐等同步 github,想修改时间轴间隔和可以导出数据@testly

仅楼主可见
testly · #70 · 2019年10月14日 Author
仅楼主可见

后排学习观摩

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册