Android 内存剖析 之 MAT讲解

简介 php

移动平台上的开发和内存管理紧密相关。尽管随着科技的进步,现今移动设备上的内存大小已经达到了低端桌面设备的水平,可是现今开发的应用程序对内存的需求也在同步增加。主要问题出在设备的屏幕尺寸上-分辨率越高须要的内存越多。熟悉Android平台的开发人员通常都知道垃圾回收器并不能完全杜绝内存泄露问题,对于大型应用而言,内存泄露对性能产生的影响是难以估量的,所以开发人员必需要有内存分析的能力。本文介绍一些有用的工具,以及如何使用它们来检测这些关键的内存泄露问题。 html

工具

有不少工具能够用来帮助检测内存泄露问题,这里列举了一些,并附上一点相应的介绍: android

工具名称: 正则表达式

简介: 编程

DDMS (Dalvik调试监视服务器) 数组

和Android一块儿推出的调试工具(android sdk的tools目录下就有)。提供端口转发服务、截屏、线程监控、堆dump、logcat、进程和无线状态监控以及一些其余功能。能够经过”./ddms”命令启动该工具,同时它也被集成在ADT中,安装之后Eclipse切到DDMS视图便可。 浏览器

MAT (内存分析工具) 服务器

快速的Java堆分析器,该工具能够检测到内存泄露,下降内存消耗,它有着很是强大的解析堆内存空间dump能力。还有不少其余功能没法在这里一一列出,能够安装一下MAT的eclipse插件试试,要活的更多详细信息请点击这里app

术语介绍

内存分析涉及到不少专用术语,他们在本文中将频繁出现,这里给出每一个术语的定义: dom

术语:

定义:

堆大小

分配给Java堆的内存,在Android平台,这些内存都是针对每一个Activity分配的(这还取决于设备)

堆转储文件

一个包含了堆中信息的二进制文件

支配树(Dominator Tree)

一个用来图形化展现对象之间关系的工具,详情请参考wiki

内存泄露

内存泄露是指有个引用指向一个再也不被使用的对象,致使该对象不会被垃圾回收器回收。若是这个对象有个引用指向一个包括不少其余对象的集合,就会致使这些对象都不会被垃圾回收。所以,须要牢记,垃圾回收没法杜绝内存泄露。

GC根

GC根是指那些假设可达的对象。 一般包括全部当前栈和系统的类加载器加载的类中引用的对象。(【译者注】栈里引用的对象是指当前执行的方法里的局部变量指向的对象,系统加载器加载的类引用的对象包括类的静态属性引用的对象)

内存泄露

内存泄露是指有个引用指向一个再也不被使用的对象,致使该对象不会被垃圾回收器回收。若是这个对象有个引用指向一个包括不少其余对象的集合,就会致使这些对象都不会被垃圾回收。

图. 1

浅堆(Shallow Heap)
被一个对象直接消耗的内存
例子:
public final class myObject { // 头部: 8字节
private int valueA; // 整型: 4字节
private int valueB; // 整型: 4字节
private char valueC[]; // 字符型数组: 4字节
}
一个myObject对象共消耗20个字节保留堆(Retained Heap)
因释放了某个对象后,能够释放的全部内存总和。
GC前:GC即将回收对象A

图.  2

GC后:回收对象A(300字节)从而致使回收对象B(50字节)和C(50字节),同时释放了对象B之后对象D也会被回收(100字节),所以回收对象A就能够释放500字节的内存,所谓保留队正式这些对象直接占用的浅堆总和。

图 3

检测内存泄露

有好几种方法能够发现内存泄露,本小节介绍其中几种。

Logcat输出的log

第一种发现内存泄露的方法是检查logcat输出的log。当垃圾回收器工做时,能够在Logcat中看到它的消息,这消息长的样子相似于:
D/dalvikm( 14302): GC_CONCURRENT freed 2349K, 65% free 3246K/9551K, external 4703K/5261K, paused 2ms+2ms

这条消息的第一个部分说明该消息产生的缘由,一共有四种类型:

GC_CONCURRENT

当堆变得很大,防止出现堆溢出异常时产生

GC_FOR_MALLOC

若是GC_CONCURENT类型的操做没有及时运行,而且应用程序还须要分配更多内存时产生。

GC_EXTERNAL_ALLOC

在Android3.0 (Honeycomb)之前,释放经过外部内存(externel memory, 经过JNI代码中malloc分配获得的内存)时产生。Android3.0和更高版本中再也不有这种类型的内存分配了。

GC_EXPLICIT

调用System.gc时产生

“freed 2349K,” – 说明释放了多少内存.
“65% free 3246K/9551K” – 65%表示目前可分配内存占比例,3426K表示当前活动对象所占内存,9551K表示堆大小。
“external 4703K/5261K” – indicates external memory allocation, how much external memory the app has allocated and the soft limit of allocation.说明外部内存的分配,已经分配了多少以及可以分配的上限。
“paused 2ms+2ms” –说明GC运行完成须要的时间。
有了这些信息,咱们就能够知道GC运行几回之后有没有成功释放出一些内存,若是分配出去的内存在这几个周期中持续增长,那么很明显存在内存泄露。下面的例子中就是存在内存泄漏时的Log。(【译者注】图片有点不清楚,可是大概能够看出来GC运行了好屡次,可分配内存比例反而从47%降到45%了)

图. 4

OutOfMemoryError 异常

当可用内存耗尽时,将会抛出OutOfMemoryError异常(OOM),此异常的出现说明有可能存在内存泄露,但这个办法并非万无一失的,由于有可能开发者就是想分配一块大内存(好比处理bitmap时),而且这一大块内存的大小超出了平台限制。可是此异常的出现至少说明开发人员必需要从新考虑本身代码对内存的使用方法是否是有问题。

跟踪内存分配状况

成功的发现内存泄露问题之后,就应该查找根源在哪里了,有两个工具能够用来辅助分析问题根源所在。

DDMS

DDMS是一个强大的工具,他能够提供有很价值的信息,它还能够生成一个HPROF dump文件,该文件可使用MAT打开分析。在Eclipse中打开DDMS,只需安装ADT插件之后打开DDMS视图便可。

图. 5

能够看到,界面至关直观,不须要什么详细的说明,但有两个标签页值得注意。第一个是“Allocation Tracker”,它用来显示当前内存分配的详细状况,能够看到每种数据类型(基本数据类型或者自定义类)的内存分配大小,甚至能够看到是哪一个源代码文件的哪一行代码分配出去的。

图. 6

第二个是“Heap”,它能够显示每一个类有多少个对象,每一个对象分配了多少内存。(【译者注】想要使用heap标签页work,要在左边窗口先选择本身想要监控的应用的package name,而后点击上面的工具栏点击那个绿色的小图标,从左往右数第二个,开启这项功能)。

图. 7

得到HPROF dump文件很容易,链接设备,启动想要监控的应用,运行一会,点击左边设备窗口上方工具栏的“Dump HPROF File”按钮(从左往右数第三个)。若是Eclipse有安装MAT插件的话,则会自动打开此dump文件。

内存分析工具 (MAT)

MAT是个强大的内存分析工具,能够单独使用也能够做为Eclipse的插件(【译者注】这个工具不在ADT中,能够在http://www.eclipse.org/mat/downloads.php下载,有stand-alone版本和Eclipse安装的update URL),这两种使用方法惟一的区别就是如何打开一个HPROF文件。独立版本须要一个打包好的HPROF文件。咱们可使用android adk自带的hprof-conv工具(在android sdk的tools目录下)打包。若是使用Eclipse插件版的MAT则不须要,直接在Eclipse中打开MAT视图便可。

概述

当打开HPROF文件后,能够看到一个Overview界面,它由如下元素构成:

  1. Overview标签页,提供一个概览界面。
  2. Histogram视图,它提供每一个类的对象统计(本文稍后详述)
  3. 支配树(Dominator Tree),提供程序中最占内存的对象 (described later in the article).
  4. 对象查询语言(Object Query Language Studio), 用来写MAT查询的工具.
  5. 专家系统测试(Expert System Test) –
    1. 堆Dump概况(Heap Dump Overview) –提供堆dump文件的详细信息
    2. 疑似泄露点(Leak Suspects) – 提供内存泄露疑点占用内存大小,被谁加载的,以及类型等详细信息。
    3. Top Components – 提供占内存最多的对象信息,还包括可能的内存浪费信息.
  6. 查询浏览器(Query Browser) – 提供不少颇有用的查询,有助于内存分析,本文将会介绍最有用的那些查询。根据地址查找对象 – 能够根据提供的一个地址查找某个特定的对象.
    1. 对象列表(List Objects) – 显示应用中全部对象,以及这些对象持有哪些其余对象和被哪些其余对象持有,(MAT会提示查询哪个对象)。
    2. 根据类显示对象(Show Objects by Class) – 列出每一个类有多少对象.
    3. 到GC根节点的路径(Path to GC Roots) – 显示到根节点的引用路径 (有好多过滤选项).
    4. 合并到GC根节点的最短路径(Merge Shortest Paths to GC Roots) –找到从GC根节点到一个对象或一组对象的共同路径。
    5. 即时支配(Immediate Dominators) – Finds and aggregates on a class level all objects dominating a given set of objects. 在给定的一组对象中,从类的层面上查找并聚合全部支配关系。(【译者注】好吧,我以为实在有必要说一下支配的意思,支配在计算机的控制流理论中意思是假如说从起始节点到节点B的全部路径都通过节点A,则节点A支配节点B。在垃圾回收理论中应该是指从某个对象在另一个对象的保留堆中)
    6. 显示保留集合(Show Retained Set) – 计算一个对象的保留堆大小.
  7. 饼图 – 显示持有内存最大的对象
  8. 直方图 – 显示每一个类的对象数量
  9. 支配树 – 列出全部对象,并按照对象持有的保留堆大小排序
  10. 检查器 – 选择一个对象,并显示其详细信息

图. 8

直方图(Histogram)

MAT最有用的工具之一,它能够列出任意一个类的实例数。查找内存泄露或者其余内存方面问题是,首先看看最有可能出问题的类,这个类有多少个实例是个比较好的选择。它支持使用正则表达式来查找某个特定的类,还能够计算出该类全部对象的保留堆最小值或者精确值。

  1. 计算保留堆大小
    a) 计算保留堆最小值(Calculate Minimum Retained Size) –计算保留堆最小值,并显示在表格中.
    b) 计算保留堆精确值(Calculate Precise Retained Size) – 计算保留堆精确值(这个过程须要一点时间) 而且显示在表格中.
  2. 正则表达式(Regex pattern) – 让用户查询某个特定的对象类

图. 9

另外,当选择了某条显示条目后,能够经过右击弹出菜单。在诊断内存相关问题时,这个菜单是个很是重要的工具。若是开发者怀疑这里有个内存泄露,能够经过菜单直接查看该类的对象持有哪些其余对象,固然,MAT支持过滤查询结果(好比说限制被持有对象的类型)。查询结果出来时,列表经过另一个有用的工具-”Path toGC Roots”-展现给开发人员。它支持多种过滤选项,好比说排除弱引用-这是最多见的一个选项,由于当GC运行时,被弱引用持有的对象会被GC直接回收,因此这种对象是不会形成内存泄露的,通常直接把这种信息排除。若是MAT预约义的查询不能知足用户需求的话,它还支持本身定制查询,定制的自由度很是大,拥有无限的可能。本文稍后会介绍如何高效的定制查询。

图. 10

图 11

支配树(Dominator Tree)

支配树能够算是MAT中第二有用的工具,它能够将全部对象按照保留堆大小排序显示。用户能够直接在“Overview”选项页中点击“Dominator Tree”进入该工具,也能够在上面提到的菜单中选择“immediate dominators”进入该工具。前者显示dump文件中全部的对象,后者会从类的层面上查找并聚合全部支配关系。支配树有如下重要属性:

  1. 属于X的子树的对象表示X的保留对象集合。
  2. 若是X是Y的持有者,那么X的持有者也是Y的持有者。
  3. 在支配树中表示持有关系的边并非和代码中对象之间的关系直接对应,好比代码中X持有Y,Y持有Z,在支配树中,X的子树中会有Z。

这三个属性对于理解支配树而言很是重要,一个熟练的开发人员能够经过这个工具快速的找出持有对象中哪些是不须要的以及每一个对象的保留堆。

图. 12

查询(Queries)

查询是用来检查对象树的基本工具,内存分析就是在许多对象中查找不但愿看到的引用关系的过程-这件事听上去容易作起来难。若是能够过滤这些对象和应用关系的话可使这项复杂的运动简单很多。一个开发人员想要成功的调试内存问题,必须掌握两个关键点。第一个是对本身的应用充分了解,若是对本身应用程序中的对象之间的关系不够了解的话,是不能找到内存问题的。第二个是掌握过滤和查找的技巧。若是开发者知道对象结构,并且也能够快速的找到想要的东西,那么找到那些异常情况将会变得容易一些。这里列出MAT工具全部内建的查询:
(【译者注】下面表格中的前两列都是MAT工具中菜单的名称)

查询:

选项:

描述:

List objects

With Outgoing References

显示选中对象持有哪些对象.

With Incoming References

显示选中对象被哪些对象持有。[若是一个类有不少不须要的实例,那么能够找到哪些对象持有该对象,让这个对象无法被回收]

Show object by class

With Outgoing References

显示选中对象持有哪些对象, 这些对象按类合并在一块儿排序

With Incoming References

显示选中对象被哪些对象持有.这些对象按类合并在一块儿排序

Path to GC Roots

With all references

显示选中对象到GC根节点的引用路径,包括全部类型引用.

Exclude weak references

显示选中对象到GC根节点的引用路径,排除了弱引用. [弱引用不会影响GC回收对象]

Exclude soft references

显示选中对象到GC根节点的引用路径,排除软引用(【译者注】软引用持有的对象在内存空间足够时,GC不回收,内存空间足够时,GC回收)

Exclude phantom references

显示选中对象到GC根节点的引用路径,排除虚引用(【译者注】虚引用是最弱的引用,get()老是返回null,当它的对象被GC回收时,GC将reference放在ReferenceQueue中,用户代码当发现这个reference在在ReferenceQueue时就知道它持有的对象已经被回收了,这时能够作一些清理工做。《Java编程思想》第四版,中文版,第87页写到Java的finilize方法是为了对象被回收前作清理工做,可是事实上会有隐患,虚引用正是弥补)

Merge Shortest Paths to GC Roots.

   选项和“Path to GC Roots”同样

显示GC根节点到选中对象的引用路径

Java Basics

References Statistics Class Loader Explorer

显示引用和对象的统计信息,列出类加载器,包括定义的类

Customized Retained Set

计算选中对象的保留堆,排除指定的引用

Open in Dominator Tree

对选中对象生成支配树

Show as Histogram

展现任意对象的直方图

Thread Details

显示线程的详细信息和属性

Thread Overview and Stacks

-

Java Collections

Array Fill Ratio

输出数组中,非基本类型、非null对象个数占数组总长度的比例。

Arrays Grouped by Size

显示数组的直方图,按大小分组

Collection Fill Ratio

输出给定集合中,非基本类型、非null对象个数占集合容量的比例。

Collections Grouped by Size

显示集合的直方图,按大小分组

Extract Hash Set Values

列出指定hash集合中的元素

Extract List Values

列出指定LinkedList,ArrayList或Vector中的元素

Hash Entries

展开显示指定HashMap或Hashtable中的键值对

Map Collision Ratio

输出指定的映射集合的碰撞率

Primitive Arrays With a Constant Value

列出基本数据类型的数组,这些数组是由一个常数填充的。

Leak Identification

Component Report Top Consumers

分析可能的内存浪费或者低效使用的组件,并输出最大的那个

报告(Reports)

MAT自带有一个报告生成系统,他能够自动分析dump文件而且生成报告给用户。第一种报告叫作“泄露疑点(Leak suspects)”,MAT分析dump文件,检查是否存在一些个头大的对象被一些引用持有保持活动状态,须要注意的是,泄露疑点并不必定是真的内存泄露。第二种报告叫作“顶级组件(Top Components)“,它包括可能的内存浪费信息,占用内存大的对象以及引用的统计信息。此报告对内存优化有很大帮助。

泄露疑点报告

泄露疑点报告包括一些潜在的内存泄露信息,再次强调一下,在这里列出的对象并不必定是真正的内存泄露,但它仍然是检查内存泄露的一个绝佳起点。报告主要包括描述和到达聚点的最短路径, 第三部分(每种类型积累的对象)主要是从第二部分衍生出来的(根据类型排序)。

图 13

“到聚点的最短路径” 很是有用,它可让开发人员快速发现究竟是哪里让这些对象没法被GC回收。

图. 14

顶级组件报告

这种报告包含不少有用的信息,特别是对那些想要下降内存使用的人来讲。它包括涉嫌内存浪费的的对象,还包括引用的统计信息。整个报告很是简单直白,所以这里不须要过多的详细介绍了。

图. 15

图. 16

使用MAT检测内存泄露

本小节主要介绍如何使用MAT检测内存泄露的实践部分,所以将会提供一段会形成内存泄露的代码做为例子。

会内存泄露的样例代码

这段代码主要为了阐述内存泄露的最主要缘由,而且为使用MAT提供一些素材。最多见的内存泄露主要有两种状况:第一种是一个静态引用持有一个普通内部类的实例(非嵌套类实例),这样会形成该实例一直处于活动状态,不会被GC回收。而后,每次屏幕旋转时,Activity的onCreate方法都会被执行,将会建立一个新的MainActivity对象,因为旧的引用,致使第一个Activity不会被垃圾回收。第二个案例被称为“context泄露”,这种状况比较难以发现,由于主要问题出在将应用的context交给一个静态属性,固然这个引用是强引用。

第一个内存泄露例子

这是第一个内存泄露例子的源代码,它会让第一个MainActivity类的实例没法让GC回收。固然这里的对象和类名称是随便取的,由于这仅仅是个一个例子,在真实项目的代码中是不会出现这种状况的。(【译者注】由于leakInstance是静态属性,它在第一次建立MainActivity实例时被赋予一个内部类对象,咱们知道一个内部类对象是能够无条件访问外围对象的全部成员的,所以这个内部类对象会持有整个MainActivity对象,致使整个MainActivity都没法被回收)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
publicclassMainActivityextendsActivity {
  //静态属性持有非静态内部类的实例--这么作很是糟糕
  staticMyLeakedClass leakInstance =null;
 
  @Override
  publicvoidonCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
 
    // Static field initialization
    if(leakInstance ==null)
      leakInstance =newMyLeakedClass();
 
    ImageView mView =newImageView(this);
    mView.setBackgroundResource(R.drawable.leak_background);
 
    setContentView(mView);
  }
 
  /*
   *非静态内部类
   */
  classMyLeakedClass {
    intsomeInt;
  }
}
首先看一下这段代码的LogCat的输出。链接设备,运行logcat,启动应用,而后旋转几回手机。图17是LogCat输出的例子:

图. 17

从第一次旋转开始,每次都存在3mb的差别。每次旋转后,堆的大小都涨3mb。这是有什么东西不对劲的第一个警告,而后LogCat没有显示任何释放已分配内存的信号。这时就须要检查应用程序内部内存的状况了,运行DDMS,获取HPROF文件,而后用自带的MAT打开。
主屏幕上的确显示了有什么东西致使内存泄露了-它大概长的和图18差很少。

图. 18

饼图上显示,有很大一部份内存被资源文件占用-这很正常,由于任何应用都有一个GUI,可是本例子只有一个资源文件,所以问题应该应该隐藏在这里。还有两个FrameLayout实例(每一个有3mb)须要检查,开发者还能够沿着一些路径还检查内存泄露问题。

基于直方图的检查

让咱们看看直方图,能够看到有大量内存被分配给了bytes和Bitmpa类型,看看一个Bitmap对象的incoming references, 结果如图19所示:

图. 19

有三个比较大的bitmap对象,这么看来这个本例最坏可能有两到三个内存泄露。这几个对象的内存大小符合LogCat的输出,让咱们在检查一下他们到GC根节点的路径(剔除全部弱应用)。第一个bitmap对象看上去没什么问题,由于它只有一个应用指向本身,没有被任何其余对象引用,并且它正在等着被垃圾回收器回收。

图. 20

 第二个对象的引用有些多,可是一切看上也也正常,看上去这是一个可见的bitmap – 这就是为何他有指向Activity和Context的引用的缘由。

图 21

看来问题就出在剩下的第三个bitmap上了。它到GC根节点只有一条路径,并且它是被“leakInstance”对象持有的,正是leakInstance对象阻止了该bitmap对象被回收。

图. 22

同时,在路径上还有一个MainActivity对象 – 看到MainActivity对象不奇怪,由于每次旋转都会新建立一个Activity,让咱们看看到底发生了什么。首先经过正则表达式过滤器在直方图中找出MainActivity对象。

图. 23

第一个不对劲的地方是有三个MainActivity实例。这固然不是说他们必定是内存泄露,但有些时候它们的生存时间倒的确比其余对象要长,因此咱们看看究竟是什么阻止它被垃圾回收。要作到这一点,首先列出全部有incoming reference的对象。再检查一下到GC根节点的路径。

图. 24

第一个MainActivity对象有一个引用指向context和ActivityThread,所以它看上去是如今正在显示的Activity。

图. 25

第二个对象只有一个引用指向本身,它正等着被垃圾回收,到目前为止,一切看上去都正常的。

图 26

如今再看第三个 – 就是它了!有个强引用指向leakInstance对象,就是它阻止了该对象被垃圾回收。

图. 27

基于支配树的检查

开发者能够经过不少种方法找到内存泄露。本文只能介绍其中几种,第二个要介绍的是基于支配树视图的。打开HPROF文件的支配树视图,按照保留堆大小进行排序。正如预料的同样,最上面的是资源类对象,还有三个FrameLayout类的对象(每一个3mb)以及一个Bitmap对象(1mb)。FrameLayout对象看上去嫌疑很大,所以咱们首先检查它们。由于支配树已经列出了具体的对象,所以咱们能够直接查看它们到GC根节点的路径。

图. 28

第一个对象就是问题所在!它到GC根节点的惟一路径正是leakInstance对象,所以它是一个泄露。

图. 29

第二个和第三个对象分别是当前正在显示和正在等着垃圾回收的。

图. 30

让咱们在看看那个bitmap对象,它也有多是一个内存泄露。选择android.graphic.Bitmap,选择显示到GC根节点的路径,剔除全部弱引用。

图. 31

bitmap类型有三个对象,每一个对象到GC根节点的路径均可以查看到,上面说的状况再次重演,三个实例中的两个很显然没问题,可是第三个对象指向leakInstance,它一直都是活动状态,不会被回收。

图. 32

可能还有上百条路径能够顺藤摸瓜找出最终的泄露点,应该选择哪条路径取决于不一样的开发者了,不一样的开发人员有对如何分析内存有着不一样的看法。

第二个内存泄露例子

第二个内存泄露场景发生在application context上。它将application context传递给一个单例模式的类,并将其做为一个属性保留下来。这个操做将会使得MainActivity没法被垃圾回收。将context做为静态属性保存也会致使一样的结果,所以这种作法应该避免。为了不重复罗嗦,这里只介绍一种查找内存泄露的方法。

  下面是代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
publicclassMainActivity2extendsActivity {
  SingletonClass mSingletonClass =null;
 
  @Override
  publicvoidonCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mSingletonClass = SingletonClass.getInstance(this);
  }
}
 
classSingletonClass {
  privateContext mContext =null;
  privatestaticSingletonClass mInstance;
 
  privateSingletonClass(Context context) {
    mContext = context;
  }
 
  publicstaticSingletonClass getInstance(Context context) {
    if(mInstance ==null) {
      mInstance =newSingletonClass(context);
    }
    returnmInstance;
  }
}
运行了上面这个应用之后,把HPROF文件弄出来,而后用MAT打开分析,咱们熟悉的概览界面又出现了:

图. 33

概览界面并无提供什么重要信息,所以开发人员须要继续本身的探索。这个例子中没有bitmap和其余资源,可是直方图显示这里有不少MainActivity对象 – 检查检查它们也许能获得更多更有价值的消息。

图. 34

将手机旋转3次,直方图显示这里有4个MainActivity对象。嗯,是时候检查是否是有哪一个对象阻止它们被回收了。要作到这一点,首先列出全部有incomming refrence的对象。只须要展开视图就很容易发现第一个对象就是当前正在显示的Activity(他包含指向ActivityThread的引用)。

图. 35

继续列出其余两个对象的到GC根节点的路径。其中一个只有一个引用指向它本身,另一个指向mInstance,该引用在SignletonClass中,还有一个应用指向当前显示的Activity(从mSigletonClass)。这正是一个泄露。

图. 36

很明显能够看出context让垃圾回收没法回收该对象。另外还有一个问题 – 每次建立一个Acitivity实例的时候,context都被传递给SingletonClass。这是个严重的问题,由于context引用指向一个不在须要的Activity,从而让这个Activity保持活跃没法被回收。在比较大的项目中,这中状况可能会致使一些意象不到的行为,而且这种问题很难被检查出来。

相关文章
相关标签/搜索