在Android开发的过程中,经常需要注意内存泄漏问题,不然很容易导致OOM问题,或者因此引起频繁gc造成app卡顿。 下面这篇文章将分析内存泄漏的原因、Android内存管理的相关内容,并分享一些检测泄漏的方法和如何避免内存泄漏。
1. 内存泄漏的定义
Android是基于Java的,众所周知Java语言的内存管理是其一大特点, 不用像C语言那样处理对象的内存分配到回收的全部过程。在Java中我们只需要简单地新建对象就可以了, Java垃圾回收器会负责回收释放对象内存。 这么看的话,垃圾回收器会管理内存又怎么还会发生内存泄漏呢?
其实Java中的内存泄漏的定义是: 对象不再被程序所使用, 但是由于这些对象被引用着导致GC(Garbage Collector)不能回收它们。
下面这张图可以帮助我们更好地理解对象的状态,以及内存泄漏的情况
左边未引用的对象是会被GC回收的,右边被引用的对象不会被GC回收,但是未使用的对象中除了未引用的对象,还包括已被引用的一部分对象,那么内存泄漏久发生这部分已被引用但未使用的对象。
接下来还有一个疑问:未使用的对象被谁引用会让GC无法回收呢?
现在主流的程序语言的主流实现中, 是通过可达性分析(Reachability Analysis)来判断对象是否存活的。这个算法的基本思路是:通过一系列的称为“GC Roots”的对象作为起点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链时,说明此对象不可用,可以被回收了。
可以作为GC Roots的对象包括下面几种:
· 虚拟机栈中引用的对象, 一般是当前在使用中局部变量
· 方法区中类静态属性引用的对象, 就是静态变量对应的对象
· 方法区中常量引用的对象
· 本地方法栈中JNI(即一般说的Native方法)引用的对象
MAT分析内存泄漏的时候,也是查看对象到GC Roots的引用链,来定位泄漏代码的位置。
所以未使用的对象直接或间接地被GC Roots引用时会让GC无法回收,从而产生内存泄漏。
2. Android的内存管理
了解了Java的内存泄漏的起因,接下来大致了解Android中的内存管理机制。
Google在Android的官网上有这样一篇文章,初步介绍了Android是如何管理应用的进程与内存分配:http://developer.android.com/training/articles/memory.html。 Android系统的Dalvik虚拟机扮演了常规的内存垃圾自动回收的角色,Android系统没有为内存提供交换区,它使用 paging 与 memory-mapping(mmapping) 的机制来管理内存,下面简要概述一些Android系统中重要的内存管理基础概念。
分配与回收内存
每一个进程的Dalvik heap都反映了使用内存的占用范围。这就是通常逻辑意义上提到的Dalvik Heap Size,它可以随着需要进行增长,但是增长行为会有一个系统为它设定的上限。
逻辑上讲的Heap Size和实际物理意义上使用的内存大小是不对等的,Proportional Set Size(PSS)记录了应用程序自身占用以及和其他进程进行共享的内存。
Android系统并不会对Heap中空闲内存区域做碎片整理。系统仅仅会在新的内存分配之前判断Heap的尾端剩余空间是否足够,如果空间不够会触发gc操作,从而腾出更多空闲的内存空间。在Android的高级系统版本里面针对Heap空间有一个Generational Heap Memory的模型,最近分配的对象会存放在Young Generation区域,当这个对象在这个区域停留的时间达到一定程度,它会被移动到Old Generation,最后累积一定时间再移动到Permanent Generation区域。系统会根据内存中不同的内存数据类型分别执行不同的gc操作。例如,刚分配到Young Generation区域的对象通常更容易被销毁回收,同时在Young Generation区域的gc操作速度会比Old Generation区域的gc操作速度更快。如下图所示: