Android内存泄露总结(一)

来源:互联网 发布:程序员充电站 编辑:程序博客网 时间:2024/06/08 06:50

什么是内存泄露

在Java程序设计中,我们可以主动地创建对象,申请内存空间。但是我们无法像C++那样直接进行内存对象的回收,而是由JVM的垃圾回收线程无规律执行垃圾回收操作。
所以在Java中,内存泄露是一个不再使用的对象在JVM执行垃圾回收操作时无法被回收的情况

JVM回收对象内存的基本原则

JVM是否会回收内存对象分四种情况考虑。
Java可以创建4种不同的引用指向对象,不同的引用对应内存回收原则也不同。
1. 强引用
强引用就是我们平时使用的创建对象的方法。如下所示:

SampleObject mSampleObject = new SampleObject();

一般采用引用计数法或者可达性分析来判断该对象是否可以被回收。
2. 软引用
创建软引用对象:

SoftReference<SampleObject> mSoftSampleObject     = new SoftReference<>(new SampleObject());SampleObject mSampleObject = mSoftSampleObject.get();

如果一个对象只有软引用指向它,当内存空间不足时,JVM会释放软引用指向的对象内存。
3. 弱引用
创建弱引用对象:

WeakReference<SampleObject> mWeakSampleObject    = new WeakReference<>(new SampleObject());SampleObject mSampleObject = mWeakSampleObject.get();

当一个对象只有弱引用指向它时,JVM会随时释放对象所占的内存
4. 虚引用
创建虚引用:

ReferenceQueue queue = new ReferenceQueue ();PhantomReference mPhantomReference  = new PhantomReference (object, queue);

当一个对象只有虚引用指向它时,相当于没有任何引用,任何时候都可能被垃圾回收器回收。
虚引用必须和引用队列 (ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。

Java的引用强弱顺序是 强>软>弱>虚

如果一个对象同时存在多种引用的话,按引用的强弱顺序依次考虑。

引用计数法与可达性分析法

引用计数法:给每一个对象设置一个变量用来记录引用数,如果引用数为0,则该对象可以被回收。
可达性分析法:以 GC Roots作为起点,连接与GC Root引用相关联的对象。然后这个对象继续连接相关联的对象。。。。。。若一个对象无法到达GC Roots,则这个对象可以被回收。
可以做GC Roots的引用:
1. 方法中创建的局部引用。
2. 一个类中的静态属性引用以及常量属性引用。
3. native方法中的引用。

现在大部分虚拟机都使用可达性分析来判断强引用对象是否可以被回收,因为引用计数法无法正确判断多个对象互相引用时的情景。
来看下面一个类:

public class GCDemo {    private Object obj;    public void setObj(Object obj) {        this.obj = obj;    }}

创建对象并初始化成员变量,让2个对象满足互相引用这个条件

GCDemo obj1 = new GCDemo();GCDemo obj2 = new GCDemo();obj1.setObj(obj2);obj2 .setObj(obj1);

内存模型图如下:

对象互相引用MM

先用引用计数法判断:
从内存模型或者代码中可以看出,obj1对象有2个引用,obj2对象也有2个引用

当我们把2个引用设置为空,不想再使用该对象。

obj1 = null;obj2 = null;

内存模型如下

对象互相引用MM

在这种情况下,obj1和obj2的引用计数为1,无法被JVM回收。但实际上我们不想在使用这两个对象,希望JVM回收内存。所以在这种情况下引用计数是无法正确回收内存的。
下面采用可达性分析:
在引用不为空时,如图所示
可达性分析图示

将引用设置为null,如图所示:
可达性分析图示

我们可以看到,虽然2个对象互相引用,但他们与GC Roots都不可达,所以可以被回收。

参考书籍:深入理解Java虚拟机:JVM高级特性与最佳实践(第2版)

0 0