Java中有一个java.lang.ref.*包,该包提供了引用对象类,支持在某种程度上与垃圾回收器之间的交互(通过引用对象类判断对象被垃圾回收器处理的状态)。
引用的抽象接口:java.lang.ref.Reference
Reference就是引用的意思,Java中的引用从垃圾回收的角度来看分为:
强引用(java.lang.ref.FinalReference)
软引用(java.lang.ref.SoftReference)
弱引用(java.lang.ref.WeakReference)
虚引用(java.lang.ref.PhantomReference)
这四种引用强度依次减弱,而且一个对象可以同时拥有多种引用, 并且可以通过Reference.get()方法获取。
1)强引用
强引用是Java中最常见、使用最多的引用,把一个对象赋给一个引用变量的普通引用就是强引用。例如:
Object o = new Object();
o就是一个强引用,它处于可达状态,强引用不会被JVM垃圾回收,即使内存不够抛出OutOfMemoryError也不会被回收,即使该对象以后永远都不会被用到JVM也不会回收。因此强引用是造成OOM的主要原因之一。
注意:强引用在Java中没有对应的实现类,总结而言,强引用有如下特点:
强引用可以直接访问目标对象
强引用所指向的对象在任何时候都不会被JVM回收
强引用可能导致内存泄漏
2)软引用
软引用是Java中一个类,需要用SoftReference类来实现,它保存的引用只有在内存不够的时候才会被JVM回收,言外之意就是当系统内存足够时它不会被回收,当系统内存空间不足时它才会被回收,所以软引用通常用在对内存敏感的程序中(常用来作缓存的设计)。
软引用可以和ReferenceQueue一起使用,当SoftReference的Referent被回收以后,这个SoftReference会被自动加入到ReferenceQueue中。也就是说,当垃圾回收器决定对软引用对象进行回收时,会先清空它的SoftReference,SoftReference的get()方法将会返回null,然后再调用对象的finalize()方法,并在下一轮GC中对其真正进行回收。
public class Student {
String id;
String name;
String age;
//getter setter略
}
package com.xf.web;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
public class SoftReferenceTest {
public static void main(String[] args) throws InterruptedException {
Student a = new Student();
a.setName("张三");
ReferenceQueue<Student> rq = new ReferenceQueue<Student>();
SoftReference<Student> weak = new SoftReference<Student>(a, rq);
a = null;
System.out.println(weak.get().getName()); //张三
System.gc();
System.out.println(weak.get().getName()); //虽然gc 但是内存充足 仍然输出张三
cleanHeapFunction(); //调用该方法 让内存不足
System.out.println(weak.get()); // null
Reference<? extends Student> rs = rq.poll(); //可以获取实例
System.out.println(rs); // java.lang.ref.SoftReference@19e0bfd
System.out.println(rs.get()); //null
}
/**
* 使用所有堆 让内存不足
*/
public static void cleanHeapFunction(){
try{
StringBuffer bf = new StringBuffer();
for(int i=0;i<10000000000L;i++){
bf.append(i);
bf.append(bf);
}
}catch(Error e){
}
}
}
所以,软引用的特点可以总结如下:
软引用使用get()方法取得对象的强引用从而访问目标对象
软引用所指向的对象按照JVM的使用情况(Heap内存是否临近阈值)来决定是否回收
软引用可以避免Heap内存不足所导致的异常
3)弱引用
弱引用需要用WeakReference类来实现,它比软引用的生存期更短,只要垃圾回收机制运行,不考虑内存实际占用,不管JVM的内存空间是否充足,总会回收该对象占用的内存,所以在某些短生命周期的对象也是适合做缓存的。
WeakReference是弱于SoftReference的引用类型。弱引用的特性和基本与软引用相似,区别就在于弱引用所指向的对象只要进行系统垃圾回收,不管内存使用情况如何,永远对其进行回收(get()方法返回null)。
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
public class WeakReferenceTest {
public static void main(String[] args) throws InterruptedException {
Student a = new Student();
a.setName("张三");
ReferenceQueue<Student> rq = new ReferenceQueue<Student>();
WeakReference<Student> weak = new WeakReference<Student>(a, rq);
a = null; //定义a=null 但是我们不使用此实例
System.out.println(weak.get().getName()); //弱引用对象 输出张三
System.gc(); //调用GC
Thread.sleep(1000);
Reference<? extends Student> rs = rq.poll();
System.out.println(rs.get()); //调动gc 对象被回收 所以输出null
}
}
所以,弱引用对象的特点可以总结为:
弱引用使用get()方法取得对象的强引用从而访问目标对象
一旦系统内存回收,无论内存是否紧张,弱引用指向的对象都会被回收
弱引用也可以避免Heap内存不足所导致的异常
4)虚引用
虚引用需要PhantomReference类来实现,PhantomReference是所有“弱引用”中最弱的引用类型。
不同于软引用和弱引用,虚引用无法通过get()方法来取得目标对象的强引用从而使用目标对象,观察源码可以发现get()被重写为永远返回null。所以虚引用不会影响对象的生命周期,主要作用是跟踪对象被垃圾回收的状态(对象生命周期的一个标记),他必须与ReferenceQueue一起使用。因为它的作用就是在对象被JVM决定需要GC后, 将自己enqueue到RQ中. 他通常用来在一个对象被GC前作为一个GC的标志,以此来做一些finalize操作。
需要注意的是,虚引用在实际使用中不太普遍。