java对象的四种引用:强引用、软引用、弱引用和虚引用
在JDK1.2之前,创建的对象只有在处于可触及(reachable)的状态下,才能被程序使用。也就是说,若一个对象不被任何变量引用,那么程序就无法再使用这个对象。垃圾回收器一旦发现这些无用对象,就会对其进行回收。但是,在某些情况下,我们会希望有些对象不需要被立即回收,或者说从全局的角度来说没有立即回收的必要性。比如缓存系统的设计,在内存不吃紧或者说为了提高运行效率的情况下,一些暂时不用的对象仍然可放置在内存中,而不是立即进行回收。
为了满足这种要求,从JDK1.2版本开始,Java的设计人员把对象的引用细分为强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)和虚引用(Phantom Reference)四种级别,细分的准则是体现在被GC回收的优先级上:强引用>软引用>弱引用>虚引用。
这样,从JDK1.2开始,GC垃圾回收器回收对象时,对象的有效性分析不仅仅是需要考虑对象可达性,还需要考虑对象的引用强度,从而使程序可以更加灵活地控制对象的生命周期。可以用一个公式概括:对象的有效性=可达性+引用类型。
强引用(Strong Reference)
强引用表示一个对象处在【有用,必须】的状态,是使用最普遍的引用。如果一个对象具有强引用,那么垃圾回收器绝不会回收它。就算在内存空间不足的情况下,Java虚拟机宁可抛出OutOfMemoryError错误,使程序异常终止,也不会通过回收具有强引用的对象来解决内存不足的问题。
Student student = new Student(); // 这就是强引用
后面的软引用、弱引用和虚引用都是基于强引用的,都需要强引用的对象。意思就是说,必须先有强引用的对象,然后在这个强引用的对象上做文章,通过一定操作把它变成软引用、弱引用或虚引用的对象。
软引用(Soft Reference)
软引用表示一个对象处在【有用,但非必须】的状态。在内存空间足够的情况下,如果一个对象只具有软引用,那么垃圾回收器就不会回收它,但是如果内存空间不足,垃圾回收器就会回收这个对象(回收发生在OutOfMemoryError错误之前)。只要垃圾回收器没有回收它,这个对象就能被程序使用。
软引用可以用来实现内存敏感的高速缓存。
Student student = new Student();
SoftReference softReference = new SoftReference(student);
软引用可以和一个引用队列(Reference Queue)联合使用,如果软引用所引用的对象被垃圾回收器回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中,以便在恰当的时机将该软引用回收。但是由于GC线程的优先级较低,通常手动调用System.gc()并不能立即执行GC,因此软引用所引用的对象并不一定会被马上回收。
ReferenceQueue<String> referenceQueue = new ReferenceQueue<>();
String str = new String("abc");
SoftReference<String> softReference = new SoftReference<>(str, referenceQueue);
str = null;
// Notify GC
System.gc();
System.out.println(softReference.get()); // abc
Reference<? extends String> reference = referenceQueue.poll();
System.out.println(reference); //null
注意:软引用对象是在jvm内存不够的时候才会被回收,我们调用System.gc()方法只是起通知作用,JVM什么时候扫描回收对象是JVM自己的状态决定的。就算扫描到软引用对象也不一定会回收它,只有内存不够的时候才会回收。
if(JVM内存不足) {
// 将软引用中的对象引用置为null
str = null;
// 通知垃圾回收器进行回收
System.gc();
}
也就是说,垃圾收集线程会在虚拟机抛出OutOfMemoryError之前回收软引用对象,而且虚拟机会尽可能优先回收长时间闲置不用的软引用对象。对那些刚构建的或刚使用过的“较新的”软对象会被虚拟机尽可能保留,这就是引入引用队列ReferenceQueue的原因。
弱引用(Weak Reference)
弱引用表示一个对象处在【可能有用,但非必须】的状态。类似于软引用,但是强度比软引用更弱一些:只具有弱引用的对象拥有更短暂的生命周期。GC线程在扫描它所管辖的内存区域的过程中,一旦发现只具有弱引用的对象,就会回收掉这些被弱引用关联的对象。也就是说,无论当前内存是否紧缺,GC都会回收被弱引用关联的对象。不过,由于GC是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。
弱引用同样可以和一个引用队列(Reference Queue)联合使用,如果弱引用所引用的对象被垃圾回收器回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。
Student student = new Student();
WeakReference weakReference = new WeakReference(student);
虚引用(PhantomReference)
虚引用顾名思义,就是形同虚设。与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。
应用场景:
1 虚引用主要用来跟踪对象被垃圾回收器回收的活动。
2 虚引用与软引用和弱引用的一个区别在于:
虚引用必须和引用队列(ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。
String str = new String("abc");
ReferenceQueue queue = new ReferenceQueue();
// 创建虚引用,要求必须与一个引用队列关联
PhantomReference pr = new PhantomReference(str, queue);
小结
Java中4种引用的级别和强度由高到低依次为:强引用 -> 软引用 -> 弱引用 -> 虚引用
当垃圾回收器回收时,某些对象会被回收,某些不会被回收。垃圾回收器会从根对象Object来标记存活的对象,然后将某些不可达的对象和一些引用的对象进行回收。
如下:
引用类型 | 被垃圾回收时间 | 用途 | 生存时间 |
---|---|---|---|
强引用 | 从来不会 | 对象的一般状态 | JVM停止运行时终止 |
软引用 | 当内存不足时 | 对象缓存 | 内存不足时终止 |
弱引用 | 正常垃圾回收时 | 对象缓存 | 垃圾回收后终止 |
虚引用 | 正常垃圾回收时 | 跟踪对象的垃圾回收 | 垃圾回收后终止 |