Android - 内存泄漏 + 垃圾回收(GC)概念
Android内存泄露——全解析和处理办法
内存泄露
说到内存泄露,就不得不提到内存溢出,这两个比较容易混淆的概念,我们来分析一下。
内存泄露:程序在向系统申请分配内存空间后(new),在使用完毕后未释放。结果导致一直占据该内存单元,我们和程序都无法再使用该内存单元,直到程序结束,这是内存泄露。
内存溢出:程序向系统申请的内存空间超出了系统能给的。比如内存只能分配一个int类型,我却要塞给他一个long类型,系统就出现oom。又比如一车最多能坐5个人,你却非要塞下10个,车就挤爆了。
大量的内存泄露会导致内存溢出(oom)。
引起内存泄漏的情况
- 对于使用了BraodcastReceiver,ContentObserver,File,游标 Cursor,Stream,Bitmap等资源的使用,应该在Activity销毁时及时关闭或者注销。
- 静态内部类持有外部成员变量(或context):可以使用弱引用或使用ApplicationContext。
- 内部类持有外部类引用,异步任务中,持有外部成员变量。
- 集合中没用的对象没有及时remove。
- 不用的对象及时释放,如使用完Bitmap后掉用recycle(),再赋null。
- handler引起的内存泄漏,MessageQueue里的消息如果在activity销毁时没有处理完,就会引起内存的泄漏,可以使用弱引用解决。
- 设置过的监听不用时,及时移除。如在Destroy时及时remove。尤其以addListener开头的,在Destroy中都需要remove。
- activity泄漏可以使用LeakCanary。
内存
想要了解内存泄露,对内存的了解必不可少。
JAVA是在JVM所虚拟出的内存环境中运行的,JVM的内存可分为三个区:堆(heap)、栈(stack)和方法区(method)。
栈(stack):是简单的数据结构,但在计算机中使用广泛。栈最显著的特征是:LIFO(Last In, First Out, 后进先出)。比如我们往箱子里面放衣服,先放入的在最下方,只有拿出后来放入的才能拿到下方的衣服。栈中只存放基本类型和对象的引用(不是对象)。
堆(heap):堆内存用于存放由new创建的对象和数组。在堆中分配的内存,由java虚拟机自动垃圾回收器来管理。JVM只有一个堆区(heap)被所有线程共享,堆中不存放基本类型和对象引用,只存放对象本身。
方法区(method):又叫静态区,跟堆一样,被所有的线程共享。方法区包含所有的class和static变量。
内存泄露原因分析
在JAVA中JVM的栈记录了方法的调用,每个线程拥有一个栈。在线程的运行过程当中,执行到一个新的方法调用,就在栈中增加一个内存单元,即帧(frame)。在frame中,保存有该方法调用的参数、局部变量和返回地址。然而JAVA中的局部变量只能是基本类型变量(int),或者对象的引用。所以在栈中只存放基本类型变量和对象的引用。引用的对象保存在堆中。
当某方法运行结束时,该方法对应的frame将会从栈中删除,frame中所有局部变量和参数所占有的空间也随之释放。线程回到原方法继续执行,当所有的栈都清空的时候,程序也就随之运行结束。
而对于堆内存,堆存放着普通变量。在JAVA中堆内存不会随着方法的结束而清空,所以在方法中定义了局部变量,在方法结束后变量依然存活在堆中。
综上所述,栈(stack)可以自行清除不用的内存空间。但是如果我们不停的创建新对象,堆(heap)的内存空间就会被消耗尽。所以JAVA引入了垃圾回收(garbage collection,简称GC)去处理堆内存的回收,但如果对象一直被引用无法被回收,造成内存的浪费,无法再被使用。所以对象无法被GC回收就是造成内存泄露的原因!
垃圾回收机制
垃圾回收(garbage collection,简称GC)可以自动清空堆中不再使用的对象。在JAVA中对象是通过引用使用的。如果再没有引用指向该对象,那么该对象就无从处理或调用该对象,这样的对象称为不可到达(unreachable)。垃圾回收用于释放不可到达的对象所占据的内存。
实现思想:我们将栈定义为root,遍历栈中所有的对象的引用,再遍历一遍堆中的对象。因为栈中的对象的引用执行完毕就删除,所以我们就可以通过栈中的对象的引用,查找到堆中没有被指向的对象,这些对象即为不可到达对象,对其进行垃圾回收。
如果持有对象的强引用,垃圾回收器是无法在内存中回收这个对象。
引用类型
在JDK 1.2以前的版本中,若一个对象不被任何变量引用,那么程序就无法再使用这个对象。也就是说,只有对象处于可触及(reachable)状态,程序才能使用它。从JDK 1.2版本开始,把对象的引用分为4种级别,从而使程序能更加灵活地控制对象的生命周期。这4种级别由高到低依次为:强引用、软引用、弱引用和虚引用。
Java/Android引用类型及其使用分析
1. 强引用(Strong reference)
实际编码中最常见的一种引用类型。常见形式如:A a = new A();等。强引用本身存储在栈内存中,其存储指向对内存中对象的地址。一般情况下,当对内存中的对象不再有任何强引用指向它时,垃圾回收机器开始考虑可能要对此内存进行的垃圾回收。如当进行编码:a = null,此时,刚刚在堆中分配地址并新建的a对象没有其他的任何引用,当系统进行垃圾回收时,堆内存将被垃圾回收。
2. 软引用(Soft Reference)
软引用的一般使用形式如下:
A a = new A();
SoftReference<A> srA = new SoftReference<A>(a);
软引用所指示的对象进行垃圾回收需要满足如下两个条件:
1.当其指示的对象没有任何强引用对象指向它;
2.当虚拟机内存不足时。
因此,SoftReference变相的延长了其指示对象占据堆内存的时间,直到虚拟机内存不足时垃圾回收器才回收此堆内存空间。
3. 弱引用(Weak Reference)
同样的,软引用的一般使用形式如下:
A a = new A();
WeakReference<A> wrA = new WeakReference<A>(a);
WeakReference不改变原有强引用对象的垃圾回收时机,一旦其指示对象没有任何强引用对象时,此对象即进入正常的垃圾回收流程。
4. 虚引用(Phantom Reference)
与SoftReference或WeakReference相比,PhantomReference主要差别体现在如下几点:
1.PhantomReference只有一个构造函数
PhantomReference(T referent, ReferenceQueue<? super T> q)
2.不管有无强引用指向PhantomReference的指示对象,PhantomReference的get()方法返回结果都是null。
因此,PhantomReference使用必须结合ReferenceQueue;
与WeakReference相同,PhantomReference并不会改变其指示对象的垃圾回收时机。
内存泄露原因
如果持有对象的强引用,垃圾回收器是无法在内存中回收这个对象。
内存泄露的真因是:持有对象的强引用,且没有及时释放,进而造成内存单元一直被占用,浪费空间,甚至可能造成内存溢出!
其实在Android中会造成内存泄露的情景无外乎两种:
- 全局进程(process-global)的static变量。这个无视应用的状态,持有Activity的强引用的怪物。
- 活在Activity生命周期之外的线程。没有清空对Activity的强引用。
检查一下你的项目中是否有以下几种情况:
- Static Activities
- Static Views
- Inner Classes
- Anonymous Classes
- Handler
- Threads
- TimerTask
- Sensor Manager
详解见该文章《Android内存泄漏的八种可能》
最后推荐一个可检测app内存泄露的项目:LeakCanary(可以检测app的内存泄露)
总结
虽然现在手机内存在不停的提升,内存泄露兴许不会像dalvik时代由于虚拟机内存过小造成各种花样oom。但是过量的内存泄露依然会造成内存溢出,影响用户体验,在如今定制系统层出不穷、机型花样越来越多的情况下解决好内存泄露的问题会让适配和稳定性进一步提高!
AsyncTask
如果AsyncTask被声明为Activity的非静态的内部类,那么AsyncTask会保留一个对Activity的引用。如果Activity已经被销毁,AsyncTask的后台线程还在执行,它将继续在内存里保留这个引用,导致Activity无法被回收,引起内存泄漏。
Android - 内存泄漏 + 垃圾回收(GC)概念的更多相关文章
- Android内存使用——垃圾回收LOG,GC_CONCURRENT等的意义的说明
在调试程序的时候,经常发现GC_CONCURRENT之类的打印.在网上搜了一下,感觉说法各式各样.最后,在Google的官方网站上发现了详细介绍. Every time a garbage colle ...
- Java 类加载机制 ClassLoader Class.forName 内存管理 垃圾回收GC
[转载] :http://my.oschina.net/rouchongzi/blog/171046 Java之类加载机制 类加载是Java程序运行的第一步,研究类的加载有助于了解JVM执行过程,并指 ...
- 垃圾回收GC:.Net自己主动内存管理 上(一)内存分配
垃圾回收GC:.Net自己主动内存管理 上(一)内存分配 垃圾回收GC:.Net自己主动内存管理 上(一)内存分配 垃圾回收GC:.Net自己主动内存管理 上(二)内存算法 垃圾回收GC:.Net自己 ...
- 垃圾回收GC:.Net自己主动内存管理 上(二)内存算法
垃圾回收GC:.Net自己主动内存管理 上(二)内存算法 垃圾回收GC:.Net自己主动内存管理 上(一)内存分配 垃圾回收GC:.Net自己主动内存管理 上(二)内存算法 垃圾回收GC:.Net自己 ...
- 垃圾回收GC:.Net自己主动内存管理 上(三)终结器
垃圾回收GC:.Net自己主动内存管理 上(三)终结器 垃圾回收GC:.Net自己主动内存管理 上(一)内存分配 垃圾回收GC:.Net自己主动内存管理 上(二)内存算法 垃圾回收GC:.Net自己主 ...
- Android内存泄漏检测利器:LeakCanary
Android内存泄漏检测利器:LeakCanary MAR 28TH, 2016 是什么? 一言以蔽之:LeakCanary是一个傻瓜化并且可视化的内存泄露分析工具 为什么需要LeakCanary? ...
- [Android]Android内存泄漏你所要知道的一切(翻译)
以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/7235616.html Android内存泄漏你所要知道的一切 ...
- Java进阶 JVM 内存与垃圾回收篇(一)
JVM 1. 引言 1.1 什么是JVM? 定义 Java Vritual Machine - java 程序的运行环境(Java二进制字节码的运行环境) 好处 一次编译 ,到处运行 自动内存管理,垃 ...
- 从C#垃圾回收(GC)机制中挖掘性能优化方案
GC,Garbage Collect,中文意思就是垃圾回收,指的是系统中的内存的分配和回收管理.其对系统性能的影响是不可小觑的.今天就来说一下关于GC优化的东西,这里并不着重说概念和理论,主要说一些实 ...
随机推荐
- 【转】Windows IIS注册asp 此操作系统版本不支持此选项 错误解决方法
原文:https://blog.csdn.net/sweety820/article/details/79538973 更新Win10,原来的IIS站点访问不了,原因是因为IIS 没有.net 4.5 ...
- linux03
linux day31.正则表达式 \* ------重复0无数次 \+ ------重复1 无数次 ^ -------开头 $ -------结尾 | ------或 & ----- 与 ( ...
- Redis---ZipList(压缩列表)
1.概述 压缩列表是一块连续的内存空间,元素之间紧挨着存储,没有任何冗余空间. Redis 为了节约内存空间使用,zset 和 hash 容器对象在元素个数较少的时候,采用压缩列表 (ziplist) ...
- while循环中使用scanf函数
妈的,这scanf函数学了快10年了,怎么还会出现莫名其妙的问题?看下面的代码(VS2012环境下运行): #define _CRT_SECURE_NO_WARNINGS #include<st ...
- POJ 2610
#include<iostream> #include<iomanip> using namespace std; int main() { //freopen("a ...
- 08-03 java 继承
继承格式,优缺点,概述: /* 继承概述: 把多个类中相同的内容给提取出来定义到一个类中. 如何实现继承呢? Java提供了关键字:extends 格式: class 子类名 extends 父类名 ...
- Nginx 简易教程
Nginx 本项目是一个 Nginx 极简教程,目的在于帮助新手快速入门 Nginx. demos 目录中的示例模拟了工作中的一些常用实战场景,并且都可以通过脚本一键式启动,让您可以快速看到演示效果. ...
- Python Web框架 tornado 异步原理
Python Web框架 tornado 异步原理 参考:http://www.jb51.net/article/64747.htm 待整理
- cpu负载的探讨 (转)
文章出处:http://blog.chinaunix.net/uid-12693781-id-368837.html 摘要:确定cpu的负载的定义,帮助管理员设置cpu负载阀值,推测可能的导致cpu负 ...
- 继承extends、super、this、方法重写overiding、final、代码块_DAY08
1:Math类的随机数(掌握) 类名调用静态方法. 包:java.lang 类:Math 方法:public static double random(): Java.lang包下的类是不用导包就可 ...