1、内存了解

在Android App的性能优化的各个部分里,内存方面的知识较多且不易理解,内存的问题绝对是最令人头疼的一部分,需要对内存基础知识、内存分配、内存管理机制等非常熟悉,才能排查问题。

1.1 了解进程的地址空间

在32位操作系统中,进程的地址空间为0到4GB,这里主要说明一下Stack和Heap:

Stack空间(进栈和出栈):

由操作系统控制,其中主要存储函数地址、函数参数、局部变量等等,所以Stack空间不需要很大,一般为几MB大小。

Heap空间:

它的使用由程序员控制,程序员可以使用malloc、new、free、delete等函数调用来操作这片地址空间。Heap为程序完成各种复杂任务提供内存空间,所以空间比较大,一般为几百MB到几GB。正是因为Heap空间由程序员管理,所以容易出现使用不当导致严重问题。

进程地址空间-引用自CSDN.png

1.2 Android的内存管理

Android系统的ART和Dalvik虚拟机扮演了常规的内存垃圾自动回收的角色, 使用paging 和 memory-mapping来管理内存,这意味着不管是因为创建对象还是使用使用内存页面造成的任何被修改的内存,都会一直存在于内存中,App唯一释放内存的方法就是释放App持有的对象引用,使GC可以回收。

1.2.1 Android的应用进程按共享/私有分类如下:

首先来了解什么是共享内存

Android应用的进程都是从一个叫做Zygote的进程fork出来的。Zygote进程在系统启动并且载入通用的framework的代码与资源之后开始启动。为了启动一个新的程序进程,系统会fork Zygote进程生成一个新的进程,然后在新的进程中加载并运行应用程序的代码。这使得大多数的RAM pages被用来分配给framework的代码,同时使得RAM资源能够在应用的所有进程之间进行共享。

- 大多数static的数据被mmapped到一个进程中。这不仅仅使得同样的数据能够在进程间进行共享,而且使得它能够在需要的时候被paged out。常见的static数据包括Dalvik Code,app resources,so文件等。

- 大多数情况下,Android通过显式的分配共享内存区域(例如ashmem或者gralloc)来实现动态RAM区域能够在不同进程之间进行共享的机制。例如,Window Surface在App与Screen Compositor之间使用共享的内存,Cursor Buffers在Content Provider与Clients之间共享内存。

image.png

共享内存:Dalvik虚拟机代码、应用框架的代码、应用框架的资源

应用框架的SO库。

私有内存:应用的代码、应用的资源、应用的SO库

共享/私有内存:堆内存,其他部分

1.2.2 android进程中内存分类如下:

android进程中内存分类.png

native heap:是lib层C/C++库所占用的内存(Native代码分配的内存,虚拟机和Android框架本身也会分配),不包含dalvik实例的linux进程,/system/bin/目录下面的程序文件运行后都是以native进程形式存在的。

Dalvik heap:Dalvik虚拟机使用的内存,包含dalvik-heap和dalvik-zygote,堆内存,是java实例对象的空间

以上两个heap空间完全由程序员控制,是最主要的两块内存,另外还有下面3种:

Dalvik Other:类的数据结构和索引

so mmap:Native代码和常量

dex mmap:Java代码和常量

1.2.3 查看内存占用

通过命令行adb shell dumpsys meminfo packagename查看内存详细占用情况:

其中几个关键的数据:

Private(Clean和Dirty的):应用进程单独使用的内存,代表着系统杀死你的进程后可以实际回收的内存总量**。通常需要特别关注其中更为昂贵的dirty部分,它不仅只被你的进程使用而且会持续占用内存而不能被从内存中置换出存储。申请的全部Dalvik和本地heap内存都是Dirty的,和Zygote共享的Dalvik和本地heap内存也都是Dirty的。

Dalvik Heap:Dalvik虚拟机使用的内存,包含dalvik-heap和dalvik-zygote,堆内存,所有的Java对象实例都放在这里。

Heap Alloc:累加了Dalvik和Native的heap。

PSS:这是加入与其他进程共享的分页内存后你的应用占用的内存量,你的进程单独使用的全部内存也会加入这个值里,多进程共享的内存按照共享比例添加到PSS值中。如一个内存分页被两个进程共享,每个进程的PSS值会包括此内存分页大小的一半在内。

Dalvik Pss内存 = 私有内存Private Dirty + (共享内存Shared Dirty / 共享进程数)

TOTAL:上面全部条目的累加值,全局的展示了你的进程占用的内存情况。

ViewRootImpl:应用进程里的活动窗口视图个数,可以用来监测对话框或者其他窗口的内存泄露。

AppContexts及Activities:应用进程里Context和Activity的对象个数,可以用来监测Activity的内存泄露。

1.3 内存回收

在Android的高级系统版本里面针对Heap空间有一个Generational Heap Memory的模型,最近分配的对象会存放在Young Generation区域,当这个对象在这个区域停留的时间达到一定程度,它会被移动到Old Generation,最后累积一定时间再移动到Permanent Generation区域。系统会根据内存中不同的内存数据类型分别执行不同的gc操作。例如,刚分配到Young Generation区域的对象通常更容易被销毁回收,同时在Young Generation区域的gc操作速度会比Old Generation区域的gc操作速度更快。

2、内存测试方法

基于上面的理论学习,可以知道内存问题基本上就是三种:内存抖动、内存泄漏、内存溢出。我们测试内存的时候也主要关注这三个测试点。至于用什么方法进行测试,下面简单列举一下工具,基本上网上都有关于工具使用很详细的教程,在此不再详细述说。

2.1 检测内存抖动

内存抖动:大量的对象被创建又在短时间内马上被释放。

瞬间产生大量的对象会严重占用Young Generation的内存区域,当达到阀值,剩余空间不够的时候,也会触发GC。系统花费在GC上的时间越多,进行界面绘制或流音频处理的时间就越短。即使每次分配的对象占用了很少的内存,但是他们叠加在一起会增加Heap的压力,从而触发更多其他类型的GC。这个操作有可能会影响到帧率,并使得用户感知到性能问题。

Memory Monitor:查看整个app所占用的内存,以及发生GC的时刻,短时间内发生大量的GC操作是一个危险的信号(用于发现有没有内存泄漏和严重内存抖动)。

例如:存在内存抖动.png

2.2 检测内存泄漏

内存泄露可以引发很多的问题:

1.程序卡顿,响应速度慢(内存占用高时JVM虚拟机会频繁触发GC)

2.莫名消失(当你的程序所占内存越大,它在后台的时候就越可能被干掉。反之内存占用越小,在后台存在的时间就越长)

3.直接崩溃(OutOfMemoryError)

内存泄漏无疑会严重影响用户体验,一些本应该废弃的资源和对象无法被释放,导致手机内存的浪费,app使用的卡顿,那么如何排查内存泄漏呢?

一个terminal指令:

adb shelldumpsys meminfo (package name)

这条指令是用来查询这个进程所占用的内存的具体详情的,通过这条指令可以看到当前app在手机中占用的具体的堆内存大小,view的数量,activity的数量等等。

meminfo_内存泄漏.png

其中activity数目是非常关键的一个信息,可以帮助我们快速地检测出内存泄漏。我们可以反复地进入退出需要测试的目标activity,如果在反复进入退出之后,用terminal执行上面的语句查询当前的内存情况,如果发现activity数量一直在增长,如上图所示,APP退出后,再进入相当界面时Views和activity数量成倍地增长,则很大可能存在内存泄漏。

另外以下4个是用于定位的内存抖动和内存泄漏发生的具体位置·

Allocation Tracker:

使用此工具来追踪内存的分配.

但是事实上,通过观察这个内存曲线的增长来或者是观察allocate tracker中的allocate data数值的增长来检测是否有内存泄漏问题,不太靠谱,因为往往内存泄漏发生了,但是GC仍然可以通过回收其他对象的方式腾出空间,导致这个数据的变化基本看不出来,甚至是减小的。

Heap Tool:

查看当前内存快照,便于对比分析哪些对象有可能是泄漏了的。

Memory monitor :

如果是Dalvik内存泄漏,也可以使用Android Device Monitor dump出一份hprof文件(别忘了先手工Cause GC),生成hprof文件进行测试分析。用hprof分析工具,可以检测到泄漏的activities、分析出重复定义的字符串。

memory monitor.png

这里能够实时地显示应用程序占用的内存,很方便我们查看。总的来说,就是使用monitor memory功能监测app主进程占用的内存,触发GC操作,而后观察内存的占用情况,如果在使用的过程中内存不断增加,没有回落,很有可能发生了内存泄漏,这时候就需要对生成的HPROF文件进行深入分析了。

hprof.png使用HPROF文件分析工具标准步骤如下:

(1)、打开Captures窗口,双击你想要查看的HPROF文件,打开HPROF文件查看工具界面;

(2)、点击Android Studio主窗口右边栏上的Analyzer Tasks,默认HPROF文件分析工具会出现在HPROF文件查看工具的右边。Analyzer Tasks列表中选择你想分析的选项;

(3)、点击开始分析的按钮;

(4)、查看分析结果,点击结果中条目可在HPROF文件分析工具中查看详情。一般查看Retained Size占用最大的类,分析是否有内存泄漏。

名称

描述

Class name

类名

Total Count

该类的实例总数

Heap Count

所选择的堆中该类的实例的数量

Sizeof

单个实例所占空间大小(如果每个实例所占空间大小不一样则显示0)

Shallow Size

堆里所有实例大小总和(Heap Count * Sizeof)

Retained Size

当该对象被GC回收时,所释放掉的内存大小

Instance

具体的实例

Reference Tree

所选实例的引用,以及指向该引用的引用。

Depth

GC根节点到所选实例的最短路径的深度

Shallow Size

所选实例的大小

Dominating Size

所选实例所支配的内存大小

MAT

上述只是可以粗略的看出是不是有问题,而要知道问题出在哪里就需要借助MAT了。将生成的.hprof文件进行转换,然后使用MAT打开来分析应用的内存使用情况。通常在使用MAT打开hprof文件后,能够在首页看到Top Comnsumers和 component Report等功能,我们可以快速定位一些大块的内存消耗。

TOP列表.png

但我们在分析时会发现系统资源类占据了很大一部分内存,因此为去除这部分对分析的干扰,我们在使用AndroidSDK提供的hprof-conv转换时需要增加一个参数:

hporf- conv [-z] -z:exclude non-app heaps,such as Zygote

如果hprof文件是已经转换过的,则可以使用OQL:

//在数据中寻找应用的Application类对象,将对象地址转换为十进制后输入以下查询语句:

select * from instanceof java.langObject s where s.@objectAddress> 1107296256

//(后面那串数字应该是Application类对象的地址)

采用这两种方法后,再使用MAT来分析就可以比较容易发现自身代码的内存问题。

小结

MAT 是探索 Java 堆并发现问题和好帮手,能够迅速发现常见的图片和大数组等问题;

内存碎片问题一般隐藏在对象的地址中;

如需要测试非 Dalvik部分,有必要了解 Linux 的进程和内存原理、内存共享机制,熟悉常用命令行工具;

内存分配的最小单位是页面,通常为4KB,这个限制会引发各种问题;

2.3 检测OOM

Android系统的每个进程都有一个最大内存限制(这个阈值可以是48M、24M、16M等,视机型而定),如果申请的内存资源超过这个限制,系统就会抛出OOM错误。

PS:可以通过adb命令查看阈值

adb shell getprop | grep dalvik.vm.heapgrowthlimit

[dalvik.vm.heapgrowthlimit]: [192m]

Android 2.x系统,当dalvik allocated + external allocated + 新分配的大小 >= dalvik heap 最大值时候就会发生OOM。其中bitmap是放于external中 。

Android 4.x系统,废除了external的计数器,类似bitmap的分配改到dalvik的java heap中申请,只要allocated + 新分配的内存 >= dalvik heap 最大值的时候就会发生OOM(art运行环境的统计规则还是和dalvik保持一致)

内存溢出是程序运行到某一阶段的最终结果,直接原因是剩余的内存不能满足内存的申请,但是再分析间接原因内存为什么没有了:

内存泄漏的存在可能导致可用内存越来越少;

内存申请的峰值超过了系统时间点剩余的内存;(例如:某手机单个进程可用最大内存为192M,目前分配内存80M,此时申请5M内存,但是当前时间点整个系统可用内存只有3M,此时没有超出单个进程可用最大内存,但是OOM也会发生)

2.4 常见内存测试场景

2.4.1 按各部分内存的用途设计场景

(1)比较操作前后或不同版本的内存变化

(2)显示多张图片的前台进程

(3)多个场景来回切换

(4)长时间运行进程的内存增长

2.4.1 根据比较结果,确定问题方向

android进程中内存分类.png

(1)Dalvik Heap内存

持续增长

内存泄露 -> LeakCanary / MAT

频繁GC,大幅度波动

大量的分配和释放 -> Allocation Tracker

比以前版本稳定增长

新功能及代码改动 -> Heap Dump / MAT

Heap Alloc不变,PSS增加

可能存在内存碎片 -> Heap Dump / MAT

(2)非Heap内存

Dalvik Other

类信息

载入Class数正相关

mmaps

可执行代码

常量

3、XX银行性能评测-内存测试结果分析

3.1 总览

此次质量开放平台-评测中心(http://fit-stg1.jryzt.com/Hyperion-server/html/index.html)的性能测试的采集的内存数据主要是针对场景页面的内存占用测试,内存占用数据获取原理是从memoryinfo中获取。

从内存占用对比看,行业竞品均值为351.3M,90分位约262.6M,75分位约339.5M,中位数约426.4M,25分位约605.7M。【榕商Bank】内存占用均值为246M,表现良好,打败了行业90%以上的竞品,请继续保持哦。

image.png

3.2 启动首页加载内存问题分析--存在内存抖动

这里选取了启动加载场景来进行内存问题的分析。

实际上从上面的总览数据分析看内存占用不同场景对比很难发现内存问题,但是同一个场景内存占用曲线图是可以发现问题的,如果曲线图有锯齿形的抖动且持续上升,基本上可能存在内存问题,如下图可得出首页存在内存抖动问题

首页加载内存抖动图1.png

首页加载内存抖动图2.png

另外还需要以日志辅助分析内存问题:从日志上看,1秒甚至几百ms内就有一次GC,而且是主动GC,说明在频繁申请内存,总阻塞耗时约5001 ms

GC.png

GC日志.png

注释:

GC Reason:GC触发原因

GC_CONCURRENT:当已分配内存达到某一值时,触发并发GC;

GC_FOR_MALLOC:当尝试在堆上分配内存不足时触发的GC;系统必须停止应用程序并回收内存;

-GC_HPROF_DUMP_HEAP: 当需要创建HPROF文件来分析堆内存时触发的GC;

-GC_EXPLICIT:当明确的调用GC时,例如调用System.gc()或者通过DDMS工具显式地告诉系统进行GC操作等;

Amount freed GC:回收的内存大小

Heap stats:堆上的空闲内存百分比 (已用内存)/(堆上总内存)

Pause time:这次GC操作导致应用程序暂停的时间。关于这个暂停的时间,在2.3之前GC操作是不能并发进行的,也就是系统正在进行GC,那么应用程序就只能阻塞住等待GC结束。而自2.3之后,GC操作改成了并发的方式进行,就是说GC的过程中不会影响到应用程序的正常运行,但是在GC操作的开始和结束的时候会短暂阻塞一段时间。

4、App端内存问题排查思路:

(1)Service停止使用时,是否被销毁

(2) 当界面变为不可见时,是否释放当前界面的资源

(3)内存变少时,是否有释放内存

(4) bitmap使用完之后是否被回收

(5)是否有大量第三方库的消耗

参考:

android内存占用分析,Android App性能评测分析-内存篇相关推荐

  1. Android App性能评测分析

    Android App性能评测分析-启动时间篇 Android App性能评测分析-网络流量篇 Android App性能评测分析-内存篇 Android App性能评测分析-cpu占用篇 Andro ...

  2. Android 应用性能优化(4)---Android App性能评测分析-启动时间篇

    Android App性能评测分析-启动时间篇 1.前言 随着项目版本的迭代,App的性能问题会逐渐暴露出来,而好的用户体验与性能表现紧密相关,性能问题从应用的启动优化开始,下面会根据实际app性能测 ...

  3. Android App性能评测分析-流畅度篇

    1.前言 在手机App竞争越来越激烈的今天,Android App的各项性能特别是流畅度不如IOS,安卓基于java虚拟机运行,触控响应的延迟和卡顿比IOS系统严重得多.一些下拉上滑.双指缩放快速打字 ...

  4. Android App性能优化之内存优化

    为什么要进行内存优化? 1.App运行内存限制,OOM导致App崩溃 2.App性能:流畅性.响应速度和用户体验 Android的内存管理方式 Android系统内存分配与回收方式 ●   一个App ...

  5. Android App 性能优化系列结语篇

    Android App 性能优化系列结语篇 原文出处:http://gold.xitu.io/post/581f4ad667f3560058a33057 关于Android App的优化, 从第一篇的 ...

  6. 中小企业上云首选,华为云全新云服务器S6性能评测分析

    今天,华为云全新弹性云服务器ECS通用计算型云服务器S6(以下简称为"华为云S6云服务器")正式发布,至顶网云能力评估小组对其展开了一场新的基于业务应用的云主机测试,对这款号称&q ...

  7. .NET/C# 如何获取当前进程的 CPU 和内存占用?如何获取全局 CPU 和内存占用?

    都知道可以在任务管理器中查看进程的 CPU 和内存占用,那么如何通过 .NET 编写代码的方式来获取到 CPU 和内存占用呢? .NET 中提供了 PerformanceCounter 类型,可以用来 ...

  8. Android文件夹占用空间分析,关于Android中图片大小、内存占用和drawable文件夹研究分析...

    引用链接 结果分析 同一张图片,放在不同目录下,会生成不同大小的Bitmap Bitmap的长度和宽度越大,占用的内存就越大 图片在硬盘上占用的大小,与在内存中占用的大小完全不一样 下面我会对上面几个 ...

  9. android 内存占用工具,Android系统检测程序内存占用各种方法

    1.检查系统总内存 liuhx@uc ~ $ adb shell cat /proc/meminfo MemTotal:         840868 kB MemFree:          457 ...

最新文章

  1. 高铁转地铁不想重复安检?多地已有举措
  2. 【C 语言】数组 ( 指针数组用法 | 命令行参数处理 )
  3. visionpro定位不跟随图像_机器视觉应用越来越多!不懂怎么办?在线公开课免费学...
  4. Dev-C++ 5.11安装教程
  5. 从KPI到OKR,高阶产品人如何推动业务高速增长
  6. anaconda3安装_Anaconda3软件安装教程
  7. Postfix邮件服务系统
  8. coreldraw铺花纹_详解CorelDRAW位图图样填充
  9. android+背景虚化,android背景虚化
  10. DNF游戏三方制裁解决方案(亲测有效)虚拟机
  11. Python中len的使用方法
  12. 【程序员进阶之路】吴恩达Deeplearning.ai课程学习全体验:深度学习必备课程
  13. Skype和Lync互连互通
  14. Java安全-Springboot Javaweb开发急速入门
  15. 计算机技巧宣讲,PPT演讲技巧
  16. kali Linux命令大全
  17. Matlab求解AX=XB(手眼标定用)
  18. 安装VS2010的SP1补丁的办法
  19. Invoke 和 BeginInvoke 的真正涵义
  20. 四年级计算机使用登记表,小学音乐器材使用登记表.doc

热门文章

  1. Fastsocket:高扩展性的 Socket 以及 Linux 内核的底层网络实现
  2. 【转】5G EN-DC/NE-DC/NGEN-DC构架
  3. GitHub标星1.3W!五分钟带你搞定Linux Bash脚本使用技巧
  4. PostgreSQL的使用-01-创建一个table
  5. 惠而浦扫地机器人充不进电_【今日关注】惠而浦扫地机器人怎么样?体验2个月之肺腑之言? | 智能扫地机器人评测...
  6. 坐标偏差大_三坐标常见撞针原因,总结的太到位了!
  7. python 京东签到在哪里_python 使用selenium登陆京东签到哪京豆
  8. java webinf lib jar_java web项目中classes文件夹下的class和WEB-INF/lib中jar里的class文件加载顺序...
  9. linux disk 清除数据,如何在Linux中使用TestDisk恢复已删除的文件
  10. java 进程撤销_2020-08-12 如何优雅关闭java进程