相信看了各种开源代码的小伙伴对SoftReference和WeakReference都不陌生,这里系统整理下相关知识,并为未接触该知识的新手作为科普。
1.什么是引用?
从数据类型来理解。
Java有两大数据类型,一类是基本数据类型,另一种则是引用数据类型。
从内存方向来理解。
由于Java中没有指针(区别于C系列),指向一块内存的数据类型叫引用。
public Class A{
}
……
A a = new A();
也可以简单的理解为,当需要创建一个对象,在堆内存中为该对象划分一块内存空间,指向这块内存空间的变量叫做引用。比如上面的a
2.为什么要分不同的引用类型?
简单来说是为了对应不同的虚拟机内存回收策略,底层提供给上层的一种有限的可控内存操作手段。
3.引用的分类
在java中引用分为强引用,软引用,弱引用,虚引用。
3.1 强引用(Strong Reference)
我们最常用的引用类型,如:
Object obj = new Object()
特点:
只要强引用还存在,既对象未死,GC永远不会回收掉被引用的对象。当内存不足时,JVM情愿抛出OOM异常使程序异常终止也不会靠回收强引用的对象来解决内存不足的问题。
3.2 软引用(Soft Reference)
软引用用来描述一些还有用但并非必需的对象。对于软引用关联着的对象,在系统将要发生内存溢出异常之前,将会把这些对象列进回收范围之中进行第二次回收。如果这次回收还没有足够的内存,才会抛出内存溢出异常。
public class Test {
public static void main(String[] args) {
Object obj = new Object();
System.out.println(obj);//java.lang.Object@75b84c92
//创建软应用
SoftReference<Object> sf = new SoftReference(obj);
System.out.println(sf.get());//java.lang.Object@75b84c92
obj = null;
System.gc();
System.out.println(obj);//null
System.out.println(sf.get());//java.lang.Object@75b84c92
}
}
特点:
如果一个对象只有软引用,则在内存充足的情况下是不会回收此对象的,但是,在内部不足即将要抛出OOM异常时就会回收此对象来解决内存不足的问题。
3.3 弱引用(Weak Reference)
弱引用也是用来描述非必需对象的,但是它的强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾回收之前。当垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象
public class Test {
public static void main(String[] args) {
Object obj = new Object();
WeakReference<Object> weakReference = new WeakReference(obj);
System.out.println(weakReference.get() != null);//true
obj = null;
System.gc();
System.out.println(weakReference.get() != null);//false
}
}
在指向obj = null语句之前,Object对象有两条引用路径,其中一条为obj强引用类型,另一条为weakReference弱引用类型。此时无论如何也不会进行垃圾回收。只有执行了obj = null,Object对象的引用就只具有弱引用,此时再进行gc,可以从结果看到其被回收了。
特点:
WeakReference基本与SoftReference类似,只是回收的策略不同。
只要GC发现一个对象只有弱引用,则就会回收此弱引用对象。但是由于GC所在的线程优先级比较低,不会立即发现所有弱引用对象并进行回收。只要GC对它所管辖的内存区域进行扫描时发现了弱引用对象就进行回收。
3.4 虚引用(Phantom Reference)
它是最弱的一种引用关系。一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用关联的唯一目的就是在这个对象被垃圾回收器回收时收到一个系统通知。
public class Test {
public static void main(String[] args) {
ReferenceQueue referenceQueue = new ReferenceQueue();
Object obj = new Object();
PhantomReference<Object> phantomReference = new PhantomReference<Object>(obj, referenceQueue);
System.out.println(phantomReference.get());//null
obj = null;
System.gc();
System.out.println(phantomReference.get());//null
}
}
特点:
虚引用是每次垃圾回收的时候都会被回收,通过虚引用的get方法永远获取到的数据为null
使用上的区别:
PhantomReference必须要和ReferenceQueue联合使用,SoftReference和WeakReference可以选择和ReferenceQueue联合使用也可以不选择。