SoftReferenec和WeakReference理解

·  阅读 1465

要想更好的理解SoftReferrence和WeakReference的机制,首先要说下Java中的对象和对象引用的概念。

对象,就是类的实例;对象引用,可以理解为操作对象的标识符,类似于c语音中的指针。

举个例子

Map map = new HashMap();
复制代码

map即是对象引用;真正的对象是通过new创建的。

下面结合这个图说下对象和对象引用的相关问题

Obj1和Obj2就是通过new创建的内存,在Java中使用的是堆内存。

o1和o2就是对象引用,在Java中方法内的对象引用使用的是栈内存。在方法执行完成,就会释放。

Java中的GC主要针对的就是Obj1和Obj2这种堆内存。GC原理简单描述就是,当一个内存对象失去了所有的引用之后,GC 就可以将其回收。反过来说,如果这个对象还存在引用,那么它将不会被 GC 回收,哪怕是 Java 虚拟机抛出 OutOfMemoryError 。

Java中的内存泄漏,主要指,在内存对象明明已经不需要的时候,还仍然保留着这块内存和它的访问方式(引用)。

再举个极端的例子

Vector v = new  Vector(10);  
for (int i = 1; i < 100; i++){  
    Object o = new  Object();  
    v.add(o);  
    o = null ;  
}
复制代码

在这个例子中,代码栈中存在Vector 对象的引用 v 和 Object 对象的引用 o 。在 For 循环中,我们不断的生成新的对象,然后将其添加到 Vector 对象中,之后将 o 引用置空。问题是当 o 引用被置空后,如果发生 GC ,我们创建的 Object 对象是否能够被 GC 回收呢?答案是否定的。因为, GC 在跟踪代码栈中的引用时,会发现 v 引用,而继续往下跟踪,就会发现 v 引用指向的内存空间中又存在指向 Object 对象的引用。也就是说尽管 o 引用已经被置空,但是 Object 对象仍然存在其他的引用,是可以被访问到的,所以 GC 无法将其释放掉。如果在此循环之后, Object 对象对程序已经没有任何作用,那么我们就认为此 Java 程序发生了内存泄漏。

还有比如Android中的Activity内存泄漏,下面的情况,当Activity关闭后,也不会释放Activity占用的内存,因为静态变量activity还在引用Activity。

static Activity activity;
public void setActivity(Activity a) {
    this.activity = a;
}
复制代码

下面介绍下Java中的引用类型

  1. 强引用(默认存在)
Object obj = new Object();
复制代码

如果不将对象引用置null,那么GC是绝对不会回收内存,即使发生OOM。

Object obj = new Object();
...
obj = null;//这时候为垃圾回收器回收这个对象,至于什么时候回收,取决于垃圾回收器的算法
复制代码
  1. 软引用(SoftReference) 在内存空间足够的情况下,除非内存空间接近临界值、jvm即将抛出oom的时候,垃圾回收器才会将该引用对象进行回收,避免了系统内存溢出的情况。
String sf = new String(“SoftReference”);
SoftReference sfRefer = new SoftReference(sf);
sf = null;
复制代码
  1. 弱引用(WeakReference) 当垃圾回收器扫描到弱引用的对象的时候,不管内存空间是否足够,都会直接被垃圾回收器回收。不过也不用特别担心,垃圾回收器是一个优先级比较低的线程,因此不一定很快可以发现弱引用的对象。
String wf = new String(“WeakReference”);
WeakReference sfRefer = new WeakReference(wf);
wf = null;
复制代码

下面通过例子说明下,SoftReference释放时机。

public class JavaTest {
    public static void main(String[] args) {
        byte[] strong1 = new byte[100000000];//用来占用大量内存

        byte[] soft = new byte[900000000];
        SoftReference softReference = new SoftReference(soft);
        soft = null;

//        byte[] strong2 = new byte[900000000];

        System.out.println("SoftReference : " + softReference.get());
    }
}
// 下面是输出结果
// SoftReference : [B@1b6d3586
// 该例子中,注释掉strong2的内存申请,则内存空间没有达到上限,因此没有释放soft引用指向的内存。
复制代码
public class JavaTest {
    public static void main(String[] args) {
        byte[] strong1 = new byte[100000000];//用来占用大量内存

        byte[] soft = new byte[900000000];
        SoftReference softReference = new SoftReference(soft);
        soft = null;

        byte[] strong2 = new byte[900000000];//用来再次申请大量内存,是当前内存不够用

        System.out.println("SoftReference : " + softReference.get());
    }
}
// 下面是输出结果
// SoftReference : null
// 该例子中,打开strong2的内存申请,则内存空间达到上限,因此释放soft引用指向的内存。
复制代码
public class JavaTest {
    public static void main(String[] args) {
        byte[] strong1 = new byte[100000000];//用来占用大量内存

        byte[] soft = new byte[900000000];
        SoftReference softReference = new SoftReference(soft);
//        soft = null;//该行代码,将强引用置null,只保留软引用,才能保证内存不够时释放该内存,否则发生OOM

        byte[] strong2 = new byte[900000000];//用来再次申请大量内存,是当前内存不够用

        System.out.println("SoftReference : " + softReference.get());
    }
}
// 下面是输出结果
// Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
// Java代码创建SoftReference后,必须将soft原对象设置null
复制代码

下面看下WeakReference的释放时机

public class JavaTest {
    public static void main(String[] args) {

        byte[] strong1 = new byte[100000000];//用来占用大量内存

        byte[] weak = new byte[900000000];
        WeakReference weakReference = new WeakReference<>(weak);
        weak = null;//该行代码,将强引用置null,只保留弱引用,才能保证释放该内存,否则发生OOM

//        System.gc();//用来强制进行gc

//        byte[] strong2 = new byte[900000000];

        System.out.println("WeakReference : " + weakReference.get());
    }
}
// 下面是输出结果
// WeakReference : [B@1b6d3586
// 在正常不执行gc情况,WeakReference引用指向的内存不会被释放
复制代码
public class JavaTest {
    public static void main(String[] args) {

        byte[] strong1 = new byte[100000000];//用来占用大量内存

        byte[] weak = new byte[900000000];
        WeakReference weakReference = new WeakReference<>(weak);
        weak = null;//该行代码,将强引用置null,只保留弱引用,才能保证释放该内存,否则发生OOM

        System.gc();//用来强制进行gc

//        byte[] strong2 = new byte[900000000];

        System.out.println("WeakReference : " + weakReference.get());
    }
}
// 下面是输出结果
// WeakReference : null
// 执行gc情况,即使内存没达到上限,WeakReference引用指向的内存会被释放
复制代码
public class JavaTest {
    public static void main(String[] args) {

        byte[] strong1 = new byte[100000000];//用来占用大量内存

        byte[] weak = new byte[900000000];
        WeakReference weakReference = new WeakReference<>(weak);
        weak = null;//该行代码,将强引用置null,只保留弱引用,才能保证释放该内存,否则发生OOM

//        System.gc();//用来强制进行gc

        byte[] strong2 = new byte[900000000];

        System.out.println("WeakReference : " + weakReference.get());
    }
}
// 下面是输出结果
// WeakReference : null
// 可以看出,申请内存达到上限时,有可能执行gc,WeakReference引用指向的内存会被释放
复制代码

下面再来看下全局引用对象的情况

public class JavaTest {

    private static Map<String, WeakReference> weakMap = new HashMap<>();

    public static void main(String[] args) {

        byte[] strong1 = new byte[100000000];

        byte[] weak = new byte[900000000];
        WeakReference weakReference = new WeakReference<>(weak);
//        weak = null;
        weakMap.put("weak", weakReference);

//        System.gc();

        byte[] strong2 = new byte[900000000];

        System.out.println("WeakReference : " + weakMap.get("weak").get());
    }
}
// 下面是输出结果
// Exception in thread "main" java.lang.OutOfMemoryError: 
复制代码

下面我们将创建WeakReference的方法放到另外的方法里再看看运行效果

public class JavaTest {

    private static Map<String, WeakReference> weakMap = new HashMap<>();

    public static void main(String[] args) {

        byte[] strong1 = new byte[100000000];

        newWeak();

//        System.gc();

        byte[] strong2 = new byte[900000000];

        System.out.println("WeakReference : " + weakMap.get("weak").get());
    }

    private static void newWeak() {
        byte[] weak = new byte[900000000];
        WeakReference weakReference = new WeakReference<>(weak);
//        weak = null;
        weakMap.put("weak", weakReference);
    }
}
// 下面是输出结果
// WeakReference : null
// 可以看到在newWeak中,没有将变量weak设置成null,在内存不够时,也会释放WeakReference中的内存
// 所以可以看出,在newWeak方法结束后,weak这个引用,自动设置成了null
复制代码
分类:
Android
标签:
分类:
Android
标签: