强引用:=
软引用:SoftReference
弱引用:WeakReference
虚引用:PhantomReference
对象的可及性:
强可及对象:永远都不会被GC回收,除非OOM
软可及对象:当系统内存不足的时候,被GC回收
弱可及对象:当系统GC发现这个对象,就被回收
虚可及对象:虚引用用来检测对象是否被回收
引用队列(ReferenceQueue):
弱引用,软引用,虚引用中都存在两种构造器,第二种可以传一个引用队列,如果我们使用第二种构造参数创建引用对象时,这个引用对象就会被监听,一旦有对象被回收,所指向它的引用对象就会被添加到引用队列中,开发者可以把这个引用从队列中弹出,来判断对象是否被回收。
代码展示:
这里以String对象为例:
创建一个String对象,分别用强引用,弱引用,软引用指向这个对象
String string2=new String("abc");
//创建一个软引用,让它指向string2对象
SoftReference<String> sfr=new SoftReference<String>(string2);
//创建一个弱引用,让它指向string2对象
WeakReference<String> wrf=new WeakReference<String>(string2);
注意这里不能写成:String str="abc",因为"abc"是放在常量区中的,而gc是从堆内存中找垃圾对象。所以“abc”不会被回收
首先,我们删除强引用,主动gc,并打印软引用和弱引用的值:
string2=null; //去掉强引用
System.gc();
System.out.println("软引用所引用对象的值:"+sfr.get());
System.out.println("弱引用所引用对象的值:"+wrf.get());
打印结果:
软引用所引用对象的值:abc
弱引用所引用对象的值:abc
我们发现删除强引用后,开启gc回收,gc并不会回收被软引用指向的对象。
然后,我们把软引用清空,再次主动gc,并打印弱引用的值
sfr.clear();
System.gc();
System.out.println(wrf.get());//对于弱可及对象,当系统GC发现这个对象,就被回收
打印结果:
null
我们发现当对象只剩下弱引用时,只要gc开始回收,这个对象就会被当成垃圾对象回收。
最后,我在这里写一下引用队列监听对象回收的测试:
private static void ReferenceQueueTest() {
final ReferenceQueue<String> QUEUE=new ReferenceQueue<>();
String str=new String("abc");
//虚引用用来检测对象是否被回收
PhantomReference<String> prf=new PhantomReference<String>(str,QUEUE);
WeakReference<String> wrf=new WeakReference<String>(str,QUEUE);
new Thread(new Runnable() {
@Override
public void run() {
while(true){
Reference<? extends String> poll = QUEUE.poll();
if(poll!=null){ //对象被回收
System.out.println("--- 引用对象被回收 ---- " + poll);
System.out.println("--- 回收对象 ---- " + poll.get());
}
}
}
}).start();
try {
Thread.sleep(2000); //这里模拟对象的生存时间,两秒后强引用被删除,对象被回收
} catch (InterruptedException e) {
e.printStackTrace();
}
str=null; //删除强引用
System.gc(); //主动gc
}
打印结果:
--- 引用对象被回收 ---- java.lang.ref.WeakReference@513065ba
--- 回收对象 ---- null
--- 引用对象被回收 ---- java.lang.ref.PhantomReference@5ff781ec
--- 回收对象 ---- null
我这里用了一个子线程来等待引用对象进入引用队列,我们将String对象的强引用删除,然后主动gc,这样子String对象就会被回收。回收后,上面测试中的弱引用对象和虚引用对象就都被放入了引用队列中。我们将引用对象弹出队列,就可以检查String对象是否被回收了。