Android内存性能测试

本文涉及的产品
性能测试 PTS,5000VUM额度
简介: Android应用大部分性能问题归根结底都会成为内存的问题,今天我们就先以Out of Memory(OOM)为起点介绍一下Android内存的原理以及排查内存问题的方法。

原理



在讲OOM之前我们先来弄清楚几个概念:内存泄漏、内存抖动、内存溢出


  • 内存泄漏:内存泄漏是指没有用的对象资源仍与GC-Root保持可达路径,导致系统无法进行回收;
  • 内存抖动:内存抖动是因为大量的对象被创建又在短时间内马上被释放,导致频繁 GC;
  • 内存溢出:我们需要一定的内存大小,但是系统无法分配给我们,满足不了我们的需求,所以会导致OOM;


既然我们知道了什么是内存溢出,那它是什么时候发生的呢?答案是在虚拟机的Heap内存使用超过堆内存最大值(Max Memory Heap)的时候,那么在这里大家需要理解的第一个概念就是Dalvik(ART)虚拟机的最大堆内存。


虚拟机的堆内存最大值


在虚拟机中,Android系统给堆(Heap)内存设置了一个最大值,可以通过runtime.getruntime().maxmemory()获取。而因为游戏消耗内存特别大的原因,Android给开通了一个绿色通道,可以在manifest里面设置LargeHeap为true。


虽然我们的手机拥有动辄8GB、16GB的内存,但系统只会分给每个应用一小部分。比如Nexus7单个应用的最大可用内存是192MB,这个值一般在Android设备出厂以后就固定下来了,分这么小内存有一个重要的原因,是Android默认没有虚拟内存。在内存资源稀缺的大背景下,为了保证在极端情况下,前台App和系统还能稳定运行,就只有靠low memory killer机制。

Low Memory Killer


下面引出另一个重要概念Low Memory Killer,也是App消耗内存过大导致的另外一个结果。在手机剩余内存低于内存警戒线的时候,就会召唤Low Memory Killer这个劫富济贫的“杀手”在后台默默干活。这里需要记住一句:App占用内存越多,被Low Memory Killer处理掉的机会就越大。


如果OOM和Low Memory Killer都没有干掉你的App,那也不代表App就没有内存问题,因为还有一类问题,会直接导致App卡顿,那就是GC。


GC


最简单的理解就是没有被GC ROOT间接或直接引用的对象的内存会被回收。在具体执行中,ART和Dalvik会有很多不同,并发GC的时候ART会比Dalvik少一个stop-the-world的阶段,因此Dalvik比ART更容易产生Jank(卡顿),当然,无论ART还是Dalvik并发GC的stop-the-world的时间并不长。然而,糟糕的情况是GC for Alloc,这个情况在内存不足以分配给新的对象时触发,它stop-the-world的时间因为GC无法并发而变得更长。


那么说到底,我们还是要避免GC FOR ALLOC,跟要避免OOM一样,关键是要管理好内存。什么是管理好内存?除了减少内存的申请回收外,更重要的是减少常驻内存和避免内存泄漏,说起内存泄漏,就必须要提Activity内存泄漏。


Activity内存泄漏


因为Activity对象会间接或者直接引用View、Bitmap等,所以一旦无法释放,会占用大量内存,如下图:


微信图片_20220518230445.jpg


图片缓存


另外一个情况就是内存常驻了,而通常在常驻内存中最大的就是图片。现在很多互联网产品APP中都有大量的图片,但是这些图片在内存中的存储如果不合理就会导致Crash堆栈然后是疯狂GC,接着触发我们前面说到的GC for Alloc,导致Stop-the-world的“卡”,最后的结果就是导致功能异常,有损用户体验。


既然有这么多的损害,为什么不能把图片下载来都放到磁盘(SD Card)上呢?其实答案不难猜,放在内存中,展示起来会“快”那么一些,快的原因有如下两点:

  • 硬件快(内存本身读取、存入速度快);
  • 复用快(解码成果有效保存,复用时,直接使用解码后对象,而不是再做一次图片解码);


很多同学不知道所谓“解码”的概念,可以简单地理解,Android系统要在屏幕上展示图片的时候只认“像素缓冲”,而这也是大多数操作系统的特征。我们常见的jpg、png等图片格式,都是把“像素缓冲”使用不同的手段压缩后的结果,所以相对而言,这些格式的图片,要在设备上展示,就必须经过一次“解码”,它的执行速度会受图片压缩比、尺寸等因素影响,是影响图片展示速度的一个重要因素。


因此官方建议使用LRU算法来做图片缓存,而不是之前推荐的WeekReference,因为WeekReference会导致大量GC。另外官方也建议,把从内存淘汰的图片,降低压缩比存储到本地,以备后用。这样就可以最大限度地降低以后复用时的解码开销。


现在我们来归纳一下,内存问题主要包括常驻问题(主要是图片缓存)、泄漏问题(主要是Activity泄漏)、GC问题(关键是GC For Alloc),后果会导致App Crash、闪退、后台被杀、卡顿,而且这是各种资源类性能问题积压的最后一环。因此可见其重要性,下面,我们来介绍一下如何简单快速的检测和定位内存泄漏问题。

方案



这里介绍手工和自动两种检测方案


手工检测和定位


先介绍一个命令:


$ adb shell dumpsys meminfo (pid name)

这个命令是用来查看指定进程所占用内存的具体情况,比如当前APP在手机中占用的具体的堆内存大小、View数量、Activity数量等:


微信图片_20220518230450.jpg


其中Activities的数量是一个非常关键的信息,可以帮助我们快速找出内存泄漏的页面,我们可以反复进入待测页面,如果反复进入退出后,查询内存的占用情况,Activity数量一直在增加,那说明一定是发生内存泄漏了。


在确定了哪个页面发生内存泄漏后,用Android Studio 自带工具就可以直接分析泄漏的Activity,完全没必要再单独安装MAT了,如下图打开Android Studio 的profile进入内存模块:


微信图片_20220518230455.png

点击Dump,反复进入退出发生内存泄漏的页面


微信图片_20220518230459.jpg

勾选下面的Activity/Fragment Leaks 就可以展示出具体哪些Activity或Fragment发生了内存泄漏,右边还有具体的引用情况


微信图片_20220518230507.jpg

自动检测和定位

这里主要是通过leakcanary来实现的,leakcanary具体在客户端的接入就不多说了,可以参考官网的文档:https://square.github.io/leakcanary/getting_started/


这里主要想讲一下如何自动收集leakcanary检测出的内存泄漏信息,因为在日常测试和开发过程中,即便客户端接了内存泄漏检测的工具,但也只是作为一个debug工具,很难系统的看出某个版本的应用内存泄漏情况是如何的。


于是我们需要在业务和开发同学平时使用的过程中顺带将这些信息收集上来,在同一的平台上以版本和页面为维度去展示,可以直观的看到某个版本发生了多少次内存泄漏以及哪些页面的哪些调用栈。


首先新建一个LeakUploadService类,用来格式化内存泄漏详情以及上传到日志服务器便于快速定位,具体代码如下:

public class LeakUploadService extends DisplayLeakService {
    @Override
    protected void afterDefaultHandling(HeapDump heapDump, AnalysisResult result, String leakInfo) {
        if (!result.leakFound || result.excludedLeak) {
            return;
        }
        String className = result.className;
        String pkgName = leakInfo.trim().split(":")[0].split(" ")[1];
        String pkgVer = leakInfo.trim().split(":")[1];
        String leakDetail = leakInfo.split("\n\n")[0] + "\n\n" + leakInfo.split("\n\n")[1];
        JSONObject json = new JSONObject();
        try {
            json.put("className", className);
            json.put("app", pkgName);
            json.put("appVersion", pkgVer);
            json.put("leakDetail", leakDetail);
        } catch (JSONException e) {
            e.printStackTrace();
        }
        OkHttpClient okHttpClient = new OkHttpClient();
        MediaType mediaType = MediaType.parse("application/json; charset=utf-8");
        RequestBody requestBody = RequestBody.create(mediaType, json.toString());
        Request request = new Request.Builder()
                .url("日志上传接口")
                .post(requestBody)
                .build();
        try {
            okHttpClient.newCall(request).execute();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

然后在manifest中注册:

<service android:name="com.squareup.leakcanary.LeakUploadService"/>


最后将Service注册到监听接口:

LeakCanary.refWatcher(application).listenerServiceClass(LeakUploadService.class)


相关实践学习
通过性能测试PTS对云服务器ECS进行规格选择与性能压测
本文为您介绍如何利用性能测试PTS对云服务器ECS进行规格选择与性能压测。
相关文章
|
19小时前
|
缓存 Java 测试技术
【01】噩梦终结flutter配安卓android鸿蒙harmonyOS 以及next调试环境配鸿蒙和ios真机调试环境-flutter项目安卓环境配置-gradle-agp-ndkVersion模拟器运行真机测试环境-本地环境搭建-如何快速搭建android本地运行环境-优雅草卓伊凡-很多人在这步就被难倒了
【01】噩梦终结flutter配安卓android鸿蒙harmonyOS 以及next调试环境配鸿蒙和ios真机调试环境-flutter项目安卓环境配置-gradle-agp-ndkVersion模拟器运行真机测试环境-本地环境搭建-如何快速搭建android本地运行环境-优雅草卓伊凡-很多人在这步就被难倒了
13 3
【01】噩梦终结flutter配安卓android鸿蒙harmonyOS 以及next调试环境配鸿蒙和ios真机调试环境-flutter项目安卓环境配置-gradle-agp-ndkVersion模拟器运行真机测试环境-本地环境搭建-如何快速搭建android本地运行环境-优雅草卓伊凡-很多人在这步就被难倒了
|
1月前
|
Dart 前端开发 Android开发
【02】写一个注册页面以及配置打包选项打包安卓apk测试—开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
【02】写一个注册页面以及配置打包选项打包安卓apk测试—开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
36 1
【02】写一个注册页面以及配置打包选项打包安卓apk测试—开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
|
2月前
|
监控 Java Android开发
深入探索Android系统的内存管理机制
本文旨在全面解析Android系统的内存管理机制,包括其工作原理、常见问题及其解决方案。通过对Android内存模型的深入分析,本文将帮助开发者更好地理解内存分配、回收以及优化策略,从而提高应用性能和用户体验。
|
3月前
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
99 1
|
3月前
|
监控 Java Android开发
深入探讨Android系统的内存管理机制
本文将深入分析Android系统的内存管理机制,包括其内存分配、回收策略以及常见的内存泄漏问题。通过对这些方面的详细讨论,读者可以更好地理解Android系统如何高效地管理内存资源,从而提高应用程序的性能和稳定性。
118 16
|
3月前
|
Android开发 开发者
Android性能优化——内存管理的艺术
Android性能优化——内存管理的艺术
|
4月前
|
编解码 Android开发 UED
构建高效Android应用:从内存优化到用户体验
【10月更文挑战第11天】本文探讨了如何通过内存优化和用户体验改进来构建高效的Android应用。介绍了使用弱引用来减少内存占用、懒加载资源以降低启动时内存消耗、利用Kotlin协程进行异步处理以保持UI流畅,以及采用响应式设计适配不同屏幕尺寸等具体技术手段。
68 2
|
4月前
|
存储 前端开发 Java
Android MVVM架构模式下如何避免内存泄漏
Android采用MVVM架构开发项目,如何避免内存泄漏风险?怎样避免内存泄漏?
143 1
|
5月前
|
监控 算法 数据可视化
深入解析Android应用开发中的高效内存管理策略在移动应用开发领域,Android平台因其开放性和灵活性备受开发者青睐。然而,随之而来的是内存管理的复杂性,这对开发者提出了更高的要求。高效的内存管理不仅能够提升应用的性能,还能有效避免因内存泄漏导致的应用崩溃。本文将探讨Android应用开发中的内存管理问题,并提供一系列实用的优化策略,帮助开发者打造更稳定、更高效的应用。
在Android开发中,内存管理是一个绕不开的话题。良好的内存管理机制不仅可以提高应用的运行效率,还能有效预防内存泄漏和过度消耗,从而延长电池寿命并提升用户体验。本文从Android内存管理的基本原理出发,详细讨论了几种常见的内存管理技巧,包括内存泄漏的检测与修复、内存分配与回收的优化方法,以及如何通过合理的编程习惯减少内存开销。通过对这些内容的阐述,旨在为Android开发者提供一套系统化的内存优化指南,助力开发出更加流畅稳定的应用。
116 0
|
5月前
|
缓存 Java 测试技术
谷粒商城笔记+踩坑(11)——性能压测和调优,JMeter压力测试+jvisualvm监控性能+资源动静分离+修改堆内存
使用JMeter对项目各个接口进行压力测试,并对前端进行动静分离优化,优化三级分类查询接口的性能
159 10
谷粒商城笔记+踩坑(11)——性能压测和调优,JMeter压力测试+jvisualvm监控性能+资源动静分离+修改堆内存

热门文章

最新文章

  • 1
    Cellebrite UFED 4PC 7.71 (Windows) - Android 和 iOS 移动设备取证软件
    24
  • 2
    【03】仿站技术之python技术,看完学会再也不用去购买收费工具了-修改整体页面做好安卓下载发给客户-并且开始提交网站公安备案-作为APP下载落地页文娱产品一定要备案-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
    33
  • 3
    Android历史版本与APK文件结构
    121
  • 4
    【02】仿站技术之python技术,看完学会再也不用去购买收费工具了-本次找了小影-感觉页面很好看-本次是爬取vue需要用到Puppeteer库用node.js扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
    28
  • 5
    【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
    23
  • 6
    APP-国内主流安卓商店-应用市场-鸿蒙商店上架之必备前提·全国公安安全信息评估报告如何申请-需要安全评估报告的资料是哪些-优雅草卓伊凡全程操作
    56
  • 7
    【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
    37
  • 8
    当flutter react native 等混开框架-并且用vscode-idea等编译器无法打包apk,打包安卓不成功怎么办-直接用android studio如何打包安卓apk -重要-优雅草卓伊凡
    73
  • 9
    【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
    118
  • 10
    Android经典面试题之Kotlin中Lambda表达式和匿名函数的区别
    29