强引用
强引用是我们接触最多的引用,若果是强引用JVM宁愿抛出OOM也不愿回收具有强引用的对象。
软引用
具有软引用的对象,内存空间充足的时候,垃圾回收器不会回收,当内存空间不充足的时候,垃圾回收器回收。
public class SoftReferenceTest {
public static void main(String[] args) {
String string = new String("hello world");
SoftReference<String> reference = new SoftReference<String>(string);
string = null;
System.out.println("gc()前弱引用所指向的对象是: "+reference.get());
System.gc();//gc()不一定立刻执行垃圾回收
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("gc()后弱引用所指向的对象是: "+reference.get());
}
}
//结果(因为内存充足所以没有被回收)
gc()前弱引用所指向的对象是: hello world
gc()后弱引用所指向的对象是: hello world
弱引用
在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。
public class WeakReferenceTest {
public static void main(String[] args) {
String string = new String("hello world");
WeakReference<String> reference = new WeakReference<String>(string);
string = null;
System.out.println("gc()前弱引用所指向的对象是: " + reference.get());
System.gc();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("gc()后弱引用所指向的对象是: " + reference.get());
}
}
//结果
gc()前弱引用所指向的对象是: hello world
gc()后弱引用所指向的对象是: null
String string = "hello world";//常量池不能被回收
WeakReference<String> reference = new WeakReference<String>(string);
string = null;
System.out.println("gc()前弱引用所指向的对象是: " + reference.get());
System.gc();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("gc()后弱引用所指向的对象是: " + reference.get());
//结果
gc()前弱引用所指向的对象是: hello world
gc()后弱引用所指向的对象是: hello world
虚引用
虚引用主要用来跟踪对象被垃圾回收器回收的活动。虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列 (ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。
public class PhantomReferenceTest {
public static void main(String[] args) {
String string = new String("hello world");
//必须和引用队列一起使用
PhantomReference<String> reference = new PhantomReference<String>(string,new ReferenceQueue<String>());
string = null;
System.out.println("gc()前虚引用所指向的对象是: " + reference.get());
System.gc();//gc()不一定立刻执行垃圾回收
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("gc()后虚引用所指向的对象是: " + reference.get());
}
}
//结果
gc()前虚引用所指向的对象是: null
gc()后虚引用所指向的对象是: null
一个永远返回 null的reference 要来何用,请注意构造PhantomReference时的第二个参数 ReferenceQueue(事实上WeakReference & SoftReference 也可以有这个参数),PhantomReference唯一的用处就是跟踪referent何时被 enqueue到ReferenceQueue中。
引用队列
软引用、弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收器回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中。
//软引用和引用队列
public class ReferenceQueueTest {
public static void main(String[] args) throws InterruptedException {
Object referent = new Object();
ReferenceQueue<Object> referenceQueue = new ReferenceQueue<Object>();
SoftReference<Object> weakReference = new SoftReference<Object>(referent, referenceQueue);
System.out.println("垃圾回收器或程序是否添加该引用到引用队列: " + weakReference.isEnqueued());
Reference<? extends Object> polled = referenceQueue.poll();
System.out.println("返回队列可用的对象: " + polled);
referent = null;
System.gc();
Thread.sleep(1000);
//weakReference.enqueue();//取消注释则运行结果和弱引用一样
System.out.println("垃圾回收器及程序是否添加该引用到引用队列: " + weakReference.isEnqueued());
Reference<? extends Object> removed = referenceQueue.remove();
System.out.println("阻塞移除队列的中的引用对象: " + removed);
}
}
//结果(阻塞)
垃圾回收器或程序是否添加该引用到引用队列: false
返回队列可用的对象: null
垃圾回收器及程序是否添加该引用到引用队列: false
//弱引用和引用队列
public class WeakReferenceTest {
public static void main(String[] args) throws InterruptedException {
Object referent = new Object();
ReferenceQueue<Object> referenceQueue = new ReferenceQueue<Object>();
WeakReference<Object> weakReference = new WeakReference<Object>(referent, referenceQueue);
System.out.println("垃圾回收器或程序是否添加该引用到引用队列 : " + weakReference.isEnqueued());
Reference<? extends Object> polled = referenceQueue.poll();
System.out.println("返回队列可用的对象 : " + polled);
referent = null;
System.gc();
Thread.sleep(1000);
System.out.println("垃圾回收器及程序是否添加该引用到引用队列 : " + weakReference.isEnqueued());
Reference<? extends Object> removed = referenceQueue.remove();
System.out.println("阻塞移除队列的中的引用对象 : " + removed);
}
}
//结果
垃圾回收器或程序是否添加该引用到引用队列 : false
返回队列可用的对象 : null
垃圾回收器及程序是否添加该引用到引用队列 : true
阻塞移除队列的中的引用对象 : java.lang.ref.WeakReference@511d50c0
再谈虚引用
(1)用来实现比较精细的内存使用控制
private byte[] data = new byte[0];
private ReferenceQueue<byte[]> queue = new ReferenceQueue<byte[]>();
private PhantomReference<byte[]> ref = new PhantomReference<byte[]>(data, queue);
public byte[] get(int size) {
if (size <= 0) {
throw new IllegalArgumentException("Wrong buffer size");
}
if (data.length < size) {
data = null;
System.gc(); //强制运行垃圾回收器
try {
queue.remove(); //该方法会阻塞直到队列非空
ref.clear(); //幽灵引用不会自动清空,要手动运行
ref = null;
data = new byte[size];
ref = new PhantomReference<byte[]>(data, queue);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return data;
}
ThreadLocal中虚引用的使用参考:
解密ThreadLocal
再谈弱引用
WeakHashMap 使用 WeakReference 作为 key, 一旦没有指向 key 的强引用, WeakHashMap 在 GC 后将自动删除相关的 entry。(ThreadLocal中有WeakHashMap的使用,大家可以看看ThreadLocal的源码)
Map<Object, Object> weakHashMap = new WeakHashMap<Object, Object>();
Object key = new Object();
Object value = new Object();
weakHashMap.put(key, value);
System.out.println(weakHashMap.containsValue(value));
key = null;
System.gc();
Thread.sleep(1000);
//一旦没有指向 key 的强引用, WeakHashMap 在 GC 后将自动删除相关的 entry
System.out.println(weakHashMap.containsValue(value));
//结果
true
false
再谈软引用
一个对象具有软引用,当内存充足的时候垃圾回收器不会回收该对象,当内存不足的时候对象会被回收。这个特点所以软引用用来实现内存敏感的高速缓存。使用软引用能防止内存泄露,增强程序的健壮性。