`
yuun
  • 浏览: 58417 次
文章分类
社区版块
存档分类
最新评论

深度性能测试:功能决定现在,性能决定未来!

 
阅读更多

摘要: 深度性能测试能协助测试人员发现APP中存在的深层次性能问题,直接定位多项性能问题及瓶颈的根本原因,方便开发者快速提升APP性能表现,使得APP运行得更加稳定。MQC深度性能测试能够帮助开发者发现深层次的性能问题,更精准地定位问题。

 

功能决定现在,性能决定未来!

一、 内存泄漏  

内存泄漏是指由于代码编写不当导致不再使用的对象无法得到及时释放。内存泄漏产生的内存垃圾不仅浪费资源,拖慢运行效率,甚至还可能造成内存溢出,直接导致应用崩溃。
  
对于Android应用,比较容易发生泄漏的是Activity、Fragment对象,此类对象的共性是其都有一定的生命周期。以Activity为例,一个Activity实例的生命起始于onCreate(),终结于onDestroy()。当一个Activity不再使用时,系统会调用回调方法Activity.onDestroy()方法做一些清理操作。但是对于Activity对象本身所占内存,则完全由虚拟机的垃圾回收器来完成回收。垃圾回收器会检查该实例是否被持有强引用,如果存在指向该对象的强引用,则不会回收其所占内存空间,这块内存空间也就成了内存垃圾。由此可见内存泄漏是由不当的强引用导致的。
  
MQC支持对Activity、Fragment对象的内存泄漏检测,检测结果可在性能报告-性能问题模块查看。

1.1 对象的引用链  

从GC ROOT到泄漏对象的引用链能精准地定位导致内存泄漏的原因。对象无法被垃圾回收器回收,一定是由于GC ROOT直接或间接持有了它的强引用。
  
常见的GCROOT有:声明为static的变量,未停止的线程,Application对象,甚至是栈内存中的局部变量。

1.2 Android中常见的内存泄露

a.集合中对象没清理造成的内存泄露  

编程过程中,我们常常会把一些对象加入到集合中。在我们不再需要该对象时,如果没有及时把它从集合中清理掉,就会导致这个集合占用的内存越来越大。同时如果这个集合是静态的话,那情况就更严重了。如下的代码段中在每次启动Activity的时候都往静态集合中添加了一个对象,如果Activity被频繁启动,set将不断变大,影响APP的正常运行。

file
  
所以,集合中不再使用的对象应及时释放掉。上述代码应该在Activity的onDestroy()方法中,及时清理set里的元素,避免无用对象继续存在强引用,例如:

file
  
这样可以保证set持有的强引用都被释放。

b. 单例模式造成的内存泄漏

  
单例的静态特性使得其生命周期可能跟应用的生命周期一样长,如果使用不恰当的话,很容易造成内存泄漏。
  
如下代码是一个简单的单例模式实现:

file
  
在创建单例的时候,如果我们传入当前Activity的Context,例如:

file
  
单例testContextHelper里面一直保存着该Activity的引用,当这个Context 对应的 Activity 退出时,由于该 Context 的引用一直被单例对象持有,所以该Activity占用的内存并不会被回收,造成泄漏。在使用单例模式时,一定要避免持有短生命周期对象的引用,比如上述代码在引用Context时可以使用Application的Context代替Activity的Context,即:
file
  
因为Application在应用的运行过程中一直存在,不会退出。

c. 非静态内部类创建静态实例造成的内存泄漏

  
在启动频繁的Activity中,为了避免反复创建某些资源,提高加载速度,我们可能会在Activity内部创建一个静态实例,每次启动Activity时都会使用该实例,如下代码:

file
  
此时Activity内部有一个静态单例,且为非静态内部类的实例。由于非静态内部类默认会持有外部类的引用,并且该类创建了一个静态实例,该实例的生命周期和应用的一样长,这就导致了该静态实例一直会持有该Activity的引用,导致Activity的内存资源不能正常回收。为了避免这一问题,在使用过程中,正确的做法是将内部类设为静态类或者变成单独的类。

d. 使用handler时的内存问题

  
在Android应用中,Handler通过发送Message与其他线程交互,发出的Message被存储在目标线程的MessageQueue中的,并且Message不一定马上就被处理,驻留时间可能比较久。比如我们用Handler发送一个延时比较久的Message:

file
  
而Message中持有Handler实例的强引用,如果Message在Queue中一直存在,就会导致Handler实例无法被回收,而Handler持有Activity的强引用,Activity对象也不会被回收,这就造成了实例泄露。所以,在创建Handler时,最好使用弱引用来引用目标Activity对象,比如:

file
  
这样可以避免由于Handler持有强引用导致Activity无法回收。

e. 静态成员变量造成的内存泄露

  
如果成员变量被声明为 static,其生命周期将与整个应用进程的生命周期一样。如果静态变量直接或间接强引用了某一短生命周期对象(比如Activity),这会导致即使app切到后台,这部分内存也不会被释放。下面的错误示范代码中,在Activity启动的时候,直接将其引用赋给了静态变量obj,会导致该Activity一直不能被回收,导致内存泄露。

file
  
因此,在使用静态变量时,应该避免其持有短生命周期对象的强引用,可以使用弱引用来代替强引用。

f. 资源未关闭造成的内存泄漏

  
对于使用了BroadcastReceiver,ContentObserver,File,游标Cursor,Stream,Bitmap等资源的使用,应该在Activity销毁时及时关闭或者注销,否则这些资源将可能不会被回收,造成内存泄漏。虽然有些系统程序,它本身可以自动取消注册的(非即时),但是我们还是应该在我们的程序中明确的取消注册,程序结束时应该把所有的注册都取消掉。

1.3 MQC提供的内存泄露分析

  
MQC提供的深度性能测试能够帮助您发现并定位发生了内存泄露的地方。当发生内存泄漏时,测试报告中会给出发生泄露的内存大小,泄露类型,发生泄露的对象,以及该对象的引用链等信息,下图是MQC检测到的APP内存泄漏案例。

file
  
图中可以看到检测到了两条内存泄漏信息。并指出了泄露内存的大小和对象类型,均为Activity对象泄露。查看引用链得知,APP在ActivityManager里面持有了所有Activity的强引用,最终导致Activity退出后无法回收,属于前述介绍的集合对象使用不当造成的内存泄露。可以看到,MQC提供的内存泄漏分析能直接定位到相关代码,方便您快速修复BUG。

二、 内存溢出

  
内存溢出(OOM, Out OfMemory)是指当已存在的对象的占用了绝大部分或者全部分配给该进程的内存空间时,如果进程再申请新的内存空间,由于没有空余内存可用于分配,或可分配的内存不够满足申请者的需求,此时系统就会抛出内存溢出异常。

2.1 常见的内存溢出原因

  
很大一部分内存溢出都是由于内存泄露导致,由于已分配的内存被泄露对象占用并且无法释放,随着泄露的对象实例越来越多,导致可用内存越来越少,最终当内存耗尽时,系统就会抛出内存溢出异常。此时只要解决了内存泄露,也就解决了内存溢出。
  
另一个内存溢出的重要原因就是应用加载了多个占用内存较多的对象。比如应用在运行过程中加载并保存了多个较大的Bitmap,导致可用内存急剧减少。因此,在代码编写过程中,对于可能占据大量内存空间的对象,我们应该使用软引用或虚引用持有该对象,使得系统GC能在内存吃紧时回收该对象释放空间。并且在不使用Bitmap时,应及时recycle,主动释放内存空间。

2.2 MQC提供的内存溢出分析

  
在应用抛出内存溢出时,深度性能测试会主动捕获这一异常,给出抛出该异常的堆栈信息。并分析当前应用进程占用的总内存大小,已分配的内存大小和可用内存大小,方便开发者定位问题。
    
如下图,测试报告中首先给出发生内存溢出的机型,同时指出检测到内存溢出时应用自身和设备内存的使用情况,可以看到Native Heap和VM Heap的空余内存都已不多。打开StackTrace后,可以看到出现OOM错误的代码行,由此我们发现可能是在加载Bitmap的时候导致的内存溢出。图中红色箭头所指的地方是应用自身的代码,我们根据这些提示就能够快速找到源文件中出错的代码,立即修复。

file

三、内存抖动

  
内存抖动指的是短时间内大量对象被创建和回收。由于短时间内产生了大量的对象,需要分配大量内存,此时需要垃圾回收器(GC)频繁工作,回收不再使用的对象来腾出内存空间。GC的频繁启动占用了一定的系统资源,最终影响应用表现。

3.1 常见的内存抖动

  
常见的内存抖动主要是由于在循环或其他场合中不停地创建新对象,并且短时间内这些对象又被释放。瞬间产生大量的对象会严重占用内存区域,当达到阀值,剩余空间不够的时候触发GC。即使每次分配的对象占用了很少的内存,但是他们叠加在一起会增加Heap的压力。GC启动时会占用CPU等资源,直接导致应用运行受到影响,可能出现界面操作不流畅等现象。

3.2 MQC提供的内存抖动分析

  
MQC能够监控系统的每一次GC,并给出GC发生时的内存使用情况,如下图所示。

file
  
图中给出了3种GC发生的时刻和内存变化的曲线图。3种GC分别为:
   GC_EXPLICIT:应用主动调用System.gc()产生的GC事件

  GC_FOR_ALLOC:内存分配时,发现可用内存不够时触发的GC事件

  GC_CONCURRENT:已分配的内存大小达到某一阈值时会触发的GC事件
    
其中后两种是系统自己决定启动的GC,应用无法控制。但是我们可以优化代码,避免频繁生成和回收对象,比如不要在循环中频繁new新的对象。

四、界面卡顿

  
界面卡顿指的是短时间内界面对用户操作没有响应。应用在出现卡顿的时候,就算知道是哪个页面出了问题,但是很难定位到具体的代码。应用卡顿检测就是帮助您快速定位卡顿的具体位置,方便您进行针对性的修复。

4.1 常见的界面卡顿原因

  
Android应用的UI绘制和用户操作消息分发都发生在应用主线程,如果主线程来不及处理UI更新和响应用户操作,用户就会感觉应用发生了卡顿。因此卡顿发生时尝尝伴随着主线程阻塞。如果在主线程中进行磁盘读写、网络操作或者大量计算时,尝尝会导致主线程被阻塞,发生界面卡顿。

4.2 MQC提供的界面卡顿分析

file
  
如上图所示,在应用运行过程中出现卡顿时,MQC会记录当前卡顿的时长,例如图中为1935ms,用于给开发者评估本次卡顿的严重性,随后给出发生卡顿时系统CPU和内存的使用情况等信息辅助开发者分析问题。也会给出卡顿发生时的应用调用的完整堆栈,用于定位发生卡顿的代码,MQC同时归纳出具体的关键代码,免去开发者在大量堆栈中寻找关键行的麻烦。

五、过度绘制

  
过度绘制一般指的是屏幕上的某些区域在一帧中被多次绘制,一般是在界面的同一个地方叠加了多个控件。这样会加重GPU的工作负担,可能导致应用运行过程中频繁掉帧,影响用户体验。

5.1 过度绘制详细介绍

  
当手机开启过度绘制时,屏幕上会标记发生过度绘制的区域,并根据不同的绘制次数使用不同的颜色,颜色标识从好到差依次是:蓝色-绿色-淡红色-红色,分别代表该区域被绘制1次、2次、3次和4次。一般情况下,最好把绘制控制在2次以下,3次绘制有时候是不能避免的,尽量避免,4次的绘制基本上是不允许的。

为了减少过度绘制,开发者应减少复杂的、层级较多的布局,去掉多余的背景色。简单的界面尽量使用线性布局;较为复杂的界面可以使用相对布局,避免嵌套过多的线性布局。可以使用ViewStub来动态加载界面。

5.2 MQC提供的过度绘制分析

  
MQC实时监测界面的过度绘制指数,当该指数大于1.5时,MQC认为该界面可能需要优化。最终,测试报告中会指出应用每个界面的过度绘制指数,并配合测试视频将过度绘制指数与Activity关联起来,并告诉开发者该界面对应的Activity。如下图所示。
  
如上图所示,在应用运行过程中出现卡顿时,MQC会记录当前卡顿的时长,例如图中为1935ms,用于给开发者评估本次卡顿的严重性,随后给出发生卡顿时系统CPU和内存的使用情况等信息辅助开发者分析问题。也会给出卡顿发生时的应用调用的完整堆栈,用于定位发生卡顿的代码,MQC同时归纳出具体的关键代码,免去开发者在大量堆栈中寻找关键行的麻烦。

file

六、启动分析

  
启动分析通过分析应用启动过程产生的trace文件来得到应用的启动时间等信息。通常来说,Android应用的启动方式分为两种:冷启动和热启动。
  
冷启动:当启动应用时,后台没有该应用的进程,此时系统会创建一个新的进程分配给该应用。冷启动因为系统会创建一个新的进程分配给它,所以会先创建和初始化Application类,随后创建和初始化MainActivity类(包括一系列的测量、布局、绘制),最后显示在界面上。
  
热启动:当启动应用时,后台已有该应用的进程(例:按back键、home键,应用虽然会退出,但是该应用的进程是依然会保留在后台,可进入任务列表查看),这种启动会从已有的进程中来启动应用。热启动因为会从已有的进程中来启动,所以热启动就不会创建新的Application,而是直接创建和初始化MainActivity,而不必创建和初始化Application,因为一个应用从新进程的创建到进程的销毁,Application只会初始化一次。一般来讲,热启动时间都会在一定程度上小于冷启动时间。
  
MQC会分析应用的冷启动时间和热启动时间,给开发者作为参考。同时给出启动分阶段耗时分析、耗时方法定位、启动过程函数调用关系等更详细的信息,可以帮助开发者快速发现启动到底卡在哪了。

file

七、严苛模式(StrictMode)

  
严苛模式(StrictMode)是一个开发辅助工具,可以帮助开发者发现那些由于编码过程中不注意而造成的问题。

7.1严苛模式的详细介绍

  
StrictMode经常用于捕获那些在应用主线程中进行的磁盘读写操作和网络请求。由于应用主线程是接收UI操作消息和执行界面渲染的地方,为了使应用运行更加流畅和更快响应,请尽量不要在主线程执行磁盘操作和网络请求。当然,这也是避免系统弹出ANR对话框和提高应用稳定性的好方法。一旦检测到违反策略(policyviolation),系统将会输出一条相关的日志,其一般包含一个调用栈,来显示应用在何处发生违例。
  
注意:尽管Android设备的磁盘一般都是闪存盘,然而实际中很多设备只能以很有限的并发数来操作文件系统。虽然磁盘读写很快,但是具体过程中可能由于其他进程占用了I/O接口,等待的过程会导致整个磁盘操作流程比较慢。如果可以,请尽量假设磁盘读写是一个比较耗时的操作。
   
StricMode除了可以检测主线程的磁盘操作和网络请求以外,还可以发现主线程中执行时间较长的方法。当应用中有继承了Closeable接口的对象没有关闭的时候,例如文件流等,或者没有使用HTTPS进行网络请求,或者同一个Activity的实例太多,StrictMode都会给出提示。其能发现的错误主要包括:
a.应用在主线程中进行磁盘读写;
b.应用在主线程中进行网络请求;
c.应用在主线程中的某些自定义方法的执行时间比较长;
d.SQLCursor对象在使用之后没有关闭;
e.继承了Closeable接口的对象在使用之后没有关闭;
f. 某一Activity有较多的实例;
g.文件读取接口暴露给外部应用;
h.注册某些对象(广播接收器、观察者、Listener等)后没有取消注册;
i. 没有使用加密网络(HTTPS)进行网络数据传输。

7.2 MQC提供的严苛模式检测

  
在MQC深度性能测试检测到您的应用存在违反上述要求的时候,MQC首先会指出应用违反了哪些严苛模式的策略,随后分析问题发生时的应用堆栈信息,指出问题出现在哪儿,并统计该问题出现了多少次。针对在检测到的主线程操作(例如出现主线程磁盘操作,主线程网络操作等)时,还会给出该操作的持续时间等信息,辅助开发者评估问题的严重程度。

file
  
上图显示MQC检测到应用中存在主线程IO的情况,具体是在主线程中进行了文件读取操作,最长的一次持续了2356毫秒,测试过程中一共出现了62次磁盘读取操作。根据后面的堆栈信息,可以看到com.stephen.performance.MainActivity类里面的readFile方法是在主线程中执行的,因此这里我们就可以针对这一信息来进行修改。
  
MQC测试平台是为广大企业客户和移动开发者提供真机测试服务的云平台,拥有大量热门机型,提供7x24全天候服务。
  
我们致力于提供专业、稳定、全面、高价值的自动化测试能力,以及简单易用的使用流程、贴心的技术服务,并且帮助客户以最低的成本、最高的效率发现APP中的各类隐患(APP崩溃、各类兼容性问题、功能性问题、性能问题等),减少用户流失,提高APP质量和市场竞争力。

原文链接:https://yq.aliyun.com/articles/143174?spm=5176.8091938.0.0.tqiJWL

 

分享到:
评论

相关推荐

    2009系统架构师大会PPT:邵宗文:数据库极限性能测试修正版

    通过对邵宗文在2009年系统架构师大会上关于《数据库极限性能测试修正版》的演讲内容进行深度解析,我们可以了解到数据库性能测试对于确保应用程序稳定高效运行的重要性。从应用类型分析、数据量评估、机器配置考量等...

    局域网基本性能测试方法

    ### 局域网基本性能测试方法 #### 一、引言 随着计算机网络技术的快速发展,局域网(LAN)已成为各行各业信息化建设的基础。为了确保局域网的稳定运行和服务质量,对其进行有效的性能测试至关重要。本文将详细介绍...

    npu性能测试资源集代码

    7. **优化策略**:根据性能测试的结果,开发者可能会调整模型架构、优化算法、改善内存管理或者利用硬件特性,如量化、稀疏性或硬件加速功能,以提高NPU的性能。 8. **挑战与趋势**:NPU性能测试面临的主要挑战包括...

    软件性能测试计划模板

    《软件性能测试计划模板》是IT领域中一个重要的文档,旨在为软件开发团队提供一个全面、规范的性能测试流程框架。这份模板包含了测试策略与目标、测试范围、测试工具等关键要素,确保性能测试的全面性和有效性。 1....

    elearning性能测试总结报告

    **eLearning性能测试总结报告深度解析** 在数字化教育日益普及的今天,eLearning平台的性能稳定性和高效性显得尤为重要。本文将对一份eLearning性能测试总结报告进行深度解析,涵盖项目背景、调优过程、问题积累及...

    HBase性能深度分析

    本文将深入探讨HBase的性能测试细节,重点剖析数据插入性能,并通过实证分析揭示其背后的机制与优化策略。 #### 数据插入性能测试设计 在评估HBase的实时数据插入性能时,测试场景设计至关重要。以随机值的Rowkey...

    《LoadRunner 没有告诉你的》之五——无所不在的性能测试

    《LoadRunner 没有告诉你的》之五——无所不在的性能测试,这篇文章主要探讨了如何在软件开发的各个阶段中有效地实施性能测试,以预防和解决潜在的性能问题。性能测试的核心目标是验证系统在特定环境和负载下的性能...

    关于IO性能的测试工具

    Oracle Orion是一款功能强大的IO性能测试工具,它不仅提供了实时监控和深度分析的能力,还具备预测性分析和自动化报告等功能,能够有效地帮助IT团队监控和优化系统的IO性能。无论是对于数据库管理员、系统架构师还是...

    WebMark一个Web服务器性能测试工具.pdf

    ### WebMark:Web服务器性能测试工具的深度解析 #### 引言 随着互联网技术的飞速发展,Web服务器作为信息传递的核心组件,其性能直接影响着用户体验和网站的稳定性。因此,对Web服务器进行准确的性能测试显得尤为...

    技术测试规范(草案)

    3. 性能测试:讲解如何评估代码的性能,包括CPU使用率、内存消耗、响应时间等,使用如BenchmarkDotNet这样的工具进行性能基准测试。 4. 异常测试:设计测试用例来检查程序在异常情况下的行为,确保有适当的错误处理...

    行业分类-电子-关于吸尘器电机性能测试箱的介绍分析.rar

    《吸尘器电机性能测试箱的深度解析》 在电子行业中,吸尘器电机作为关键组件,其性能直接影响到产品的整体效能与使用寿命。因此,对吸尘器电机进行精确、全面的性能测试至关重要。本文将深入探讨“吸尘器电机性能...

    半导体行业深度跟踪报告:多维数据框架详解半导体产业链景气趋势.rar

    这份深度跟踪报告——"多维数据框架详解半导体产业链景气趋势",旨在通过全面的数据分析,揭示半导体产业链的现状、挑战与机遇。以下是对报告中可能涉及的关键知识点的详细解析: 1. **半导体产业链结构**:半导体...

    计算机行业深度研究报告:国产数据库:宽赛道、高壁垒、新机遇.rar

    报告可能详述这些壁垒,包括核心技术的研发、稳定性与性能测试、与国际标准的兼容性、客户服务与支持等。 5. **新机遇**:国产数据库面临的挑战与机遇并存。可能包括5G、人工智能等新技术带来的应用场景创新,以及...

    传统架构下应用性能测试.pptx

    随着技术的发展,性能测试已经从单一的功能验证扩展到了应对快速迭代的需求和复杂多变的系统环境。在传统架构下,性能测试面临的主要挑战包括测试场景的增加、资源的协调、测试数据的准备以及测试监控的效率和准确性...

    2023年山西省职业院校技能大赛(高职组)“软件测试”赛项竞赛规程

    2. **性能测试**:考察选手对性能测试工具(如LoadRunner、JMeter)的运用,包括性能需求分析、测试方案制定、测试执行和结果分析。这要求选手具备对系统负载和压力测试的能力。 3. **单元测试**:主要考查选手对...

    软件测试习题集及答案(详细版).pdf

    五、性能测试 1. 压力测试:检查系统在高负载下的表现和稳定性。 2. 负载测试:确定系统在正常和预期负载下的行为。 3. 耐久测试:长时间运行以检测疲劳和内存泄漏等问题。 4. 端到端响应时间:测量整个系统从请求到...

    深度学习案例:基于卷积神经网络(CNN)的图像分类.pdf

    未来的研究方向可以探索更多先进的深度学习技术,如生成对抗网络(GANs)、循环神经网络(RNNs)等,以进一步提高图像分类系统的性能和应用范围。此外,结合多模态数据(如文本、声音等)的融合技术也是一个值得研究...

Global site tag (gtag.js) - Google Analytics