Android内存优化(四)解析Memory Monitor、Allocation Tracker和Heap Dump

前言

1.Memory Monitor

在Android Studio(以下简称AS)中Android Monitor是一个主窗口,它包含了Logcat,、Memory Monitor、CPU Monitor、 GPU Monitor和Network Monitor。其中Memory Monitor可以轻松地监视应用程序的性能和内存使用情况,以便于找到被分配的对象,定位内存泄漏,并跟踪连接设备中正在使用的内存数量。Memory Monitor可以报告出你的应用程序的内存分配情况, 更形象的呈现出应用程序使用的内存。它的作用如下:

  • 实时显示可用的和分配的Java内存的图表。
  • 实时显示垃圾收集(GC)事件。
  • 启动垃圾收集事件。
  • 快速测试应用程序的缓慢是否与过度的垃圾收集事件有关。
  • 快速测试应用程序崩溃是否与内存耗尽有关。

1.1 使用Memory Monitor

在使用Memory Monitor之前要确保手机开启了开发者模式和USB调试。 使用的步骤为: 1.运行需要监控的应用程序。 2.点击AS面板下面的Android图标,并选择Monitors选项。 如果Memory Monitor已经运行,效果如下图所示(AS版本2.3.2)。

图中的标注的功能如下:

  • Initiate GC(标识1):用来手动触发GC。
  • Dump Java heap(标识2):保存内存快照。
  • Start/Stop Allocation Tracking(标识3):打开Allocation Tracker工具(后面会介绍)。
  • Free(标识4):当前应用未分配的内存大小。
  • Allocated(标识5):当前应用分配的内存大小。

图中y轴显示当前应用的分配的内存和未分配的内存大小;x轴表示经过的时间。

1.2 大内存申请与GC

从上图可以看出,分配的内存急剧上升,这就是大内存分配的场景,我们要判断这是否是合理的分配的内存,是Bitmap还是其他的大数据,并且对这种大数据进行优化,减少内存开销。 接下来分配的内存出现急剧下降,这表示垃圾收集事件,用来释放内存。

1.3 内存抖动

内存抖动一般指在很短的时间内发生了多次内存分配和释放,严重的内存抖动还会导致应用程序卡顿。内存抖动出现原因主要是短时间频繁的创建对象(可能在循环中创建对象),内存为了应对这种情况,也会频繁的进行GC,因此综合起来就产生了内存抖动,产生了如上图般的锯齿状。

2.Allocation Tracker

Allocation Tracker用来跟踪内存分配,它允许你在执行某些操作的同时监视在何处分配对象,了解这些分配使你能够调整与这些操作相关的方法调用,以优化应用程序性能和内存使用。 Allocation Tracker能够做到如下的事情:

  • 显示代码分配对象类型、大小、分配线程和堆栈跟踪的时间和位置。
  • 通过重复的分配/释放模式帮助识别内存变化。
  • 当与 HPROF Viewer结合使用时,可以帮助你跟踪内存泄漏。例如,如果你在堆上看到一个bitmap对象,你可以使用Allocation Tracker来找到其分配的位置。

2.1 使用Allocation Tracker

AS和DDMS中都有Allocation Tracker,这里会·介绍AS中的Allocation Tracke如何使用。首先要确保要确保手机开启了开发者模式,并且开启了USB调试。 使用的步骤为: 1.运行需要监控的应用程序。 2.点击AS面板下面的Android图标,并选择Monitors选项。 3.点击Start Allocation Tracking按钮,这时Start Allocation Tracking按钮变为了Stop Allocation Tracking按钮。 4.操作应用程序。 5.点击Stop Allocation Tracking按钮,结束快照。这时Memory Monitor会显示出捕获快照的期间,如下图所示。

6.过几秒后就会自动打开一个窗口,显示当前生成的alloc文件的内存数据。

2.2 alloc文件分析

自动打开的alloc文件窗口如下图所示。

该alloc文件显示以下信息:

说明

Method

负责分配的Java方法

Count

分配的实例总数

Total Size

分配内存的总字节数

接着我们来分析标红框的内容,负责分配的Java方法为performLaunchActivity,内存分配序列为2369,分配的对象为ActivityThread,分配的实例总数为300个,分配内存的总字节数为10512。不了解performLaunchActivity方法和ActivityThread可以看Android深入四大组件这一系列的文章。

目前的菜单选项是Group by Method我们也可以选择 Group By Allocator,如下图所示。

为了更好的解释图中的信息,这里给出测试的代码,MainActivity和SecondActivity 的代码如下所示。 MainActivity.java

SecondActivity.java

其中SecondActivity是存在内存泄漏的,生成快照期间,我的操作就是在MainActivity和SecondActivity跳转了3次(点击button 共6次)。这时我们回过头来看上图的红框的信息,MainActivity总共分配了3个Intent实例,占用内存为192字节。SecondActivity总共分配了6个实例,占用内存为96字节,其中分配了3个匿名内部类OnClickListener的实例,3个InnerClass的实例。

我们可以选择列表中的一项,单击鼠标右键,在弹出的菜单中选择jump to the source就可以跳转到对应的源文件中。 除此之外,还可以点击Show/Hide Chart按钮来显示数据的图形化,如下图所示。

3.Heap Dump

Heap Dump的主要功能就是查看不同的数据类型在内存中的使用情况。它可以帮助你找到大对象,也可以通过数据的变化发现内存泄漏。

3.1 使用Heap Dump

打开Android Device Monitor工具,在左边Devices列表中选择要查看的应用程序进程,点击Update Heap按钮(装有一半绿色液体的圆柱体),在右边选择Heap选项,并点击Cause GC按钮,就会开始显示数据。我们每次点击Cause GC按钮都会强制应用程序进行垃圾回收,并将清理后的数据显示在Heap工具中。如下图所示。

从上图可以看出,Heap工具共有三个区域,分别是总览视图(标识1)、详情视图(标识2)和内存分配柱状图(标识2)。

3.2 总览视图

其中总览视图可以查看整体的内存情况,表中的显示信息如下所示。

说明

Heap Size

堆栈分配给该应用程序的内存大小

Allocated

已分配使用的内存大小

Free

空闲的内存大小

%Used

当前Heap的使用率(Allocated/Heap Size)

#Objects

对象的数量

结合上表和上图,我们在总览视图获得的信息就是:堆栈分配给当前的应用程序的内存大小为2.346MB,已分配的内存为1.346MB,空闲的内存为1MB,当前Heap的使用率为57.37%,对象的数量为24058个。

3.3 详情视图

详细视图展示了所有的数据类型的内存情况,表中列的信息如下所示。

说明

Type

数据类型

Total Size

总共占用的内存大小

Smallest

将该数据类型的对象从小到大排列,排在第一个的对象所占用的内存

Largest

将该数据类型的对象从小到大排列,排在最后一个的对象所占用的内存

Median

将该数据类型的对象从小到大排列,排在中间的对象所占用的内存

Average

该数据类型的对象所占用内存的平均值

除了列的信息,还有行信息:

说明

free

内存碎片

data object

对象

class object

1-byte array (byte[],boolean[])

1字节的数组对象

2-byte array (short[],char[])

2字节的数组对象

4-byte array (object[],int[],float[])

4字节的数组对象

6-byte array (long[],double[])

8字节的数组对象

non-Java object

非Java对象

行信息中比较重要的是free,它与总览视图中的free的含义不同,它代表内存碎片。当新创建一个对象时,如果碎片内存能容下该对象,则复用碎片内存,否则就会从free空间(总览视图中的free)重新划分内存给这个新对象。free是判断内存碎片化程度的一个重要的指标。 此外,1-byte array这一行的信息也很重要,因为图片是以byte[]的形式存储在内存中的,如果1-byte array一行的数据过大,则需要检查图片的内存管理了。

3.4 检测内存泄漏

Heap Dump也可以检测内存泄漏。在左边Devices列表中选择要查看的应用程序进程,点击Update Heap按钮(装有一半绿色液体的圆柱体),在右边选择Heap选项,并点击Cause GC按钮,就会开始显示数据,如下图所示。

这时data object的Total Size为270.266KB。接下来操作应用,这个应用仍旧是在2.2小节所举的内存泄漏的例子,我反复的在MainActivity和SecondActivity跳转了10次(点击Button共20次),数据显示为:

data object的Total Size变为了768.172KB。这时我点击Cause GC按钮,数据显示为:

可以看到data object的Total Size变为了444.516KB,再点击一次Cause GC按钮:

Total Size变为了323.312KB,经过两次Cause GC的操作,Total Size的值从768.172KB变为了323.312KB,这是一个比较大的变化,说明在Cause GC操作之前有462.86KB(768.172KB-323.312KB)的内存没有被回收,可能发生了内存泄漏。

原文发布于微信公众号 - 刘望舒(liuwangshuAndroid)

原文发表时间:2017-07-13

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏CDA数据分析师

深入解读Python解析XML的几种方式

本文将介绍深入解读利用Python语言解析XML文件的几种方式,并以笔者推荐使用的ElementTree模块为例,演示具体使用方法和场景。文中所使用的Pytho...

30170
来自专栏Jacklin攻城狮

iOS,面试必看,最全梳理

目前形势,参加到iOS队伍的人是越来越多,甚至已经到供过于求了。今年,找过工作人可能会更深刻地体会到今年的就业形势不容乐观,加之,培训机构一火车地向用人单位输送...

25220
来自专栏玄魂工作室

PHP代码审计Day2 - filter_var函数缺陷

--------------------------------------------------------------------------------...

13820
来自专栏恰童鞋骚年

.NET基础拾遗(5)多线程开发基础

  下面的一些基本概念可能和.NET的联系并不大,但对于掌握.NET中的多线程开发来说却十分重要。我们在开始尝试多线程开发前,应该对这些基础知识有所掌握,并且能...

13720
来自专栏Crossin的编程教室

像对象一样对待数据

咱们编程教室有不少同学,学完了基础课程,掌握了一定的编程能力,开始做项目了。然后很可能遇到一个问题:管理数据。课程里有讲过用文件保存数据,还有 pickle、c...

10520
来自专栏Play & Scala 技术分享

Play Scala 2.5.x - Play with MongoDB 开发指南

34250
来自专栏Hongten

JSP 三讲

50910
来自专栏Python爱好者

Java基础笔记11

14340
来自专栏一个会写诗的程序员的博客

《Kotin 极简教程》第9章 轻量级线程:协程(2)《Kotlin极简教程》正式上架:

如果需要依次调用它们, 我们只需要使用正常的顺序调用, 因为协同中的代码 (就像在常规代码中一样) 是默认的顺序执行。下面的示例通过测量执行两个挂起函数所需的总...

14120
来自专栏Android-JessYan

你不知道的Retrofit缓存库RxCache前言简介使用详解总结

21420

扫码关注云+社区

领取腾讯云代金券