顶层类是java.lang.ref.Reference
,注意是一个抽象类,而不是接口,其中比较重要的引用队列ReferenceQueue就在该类中定义,子类们共同使用:
// java.lang.ref.Reference
public abstract class Reference<T> {
// ...
volatile ReferenceQueue<? super T> queue;
// ...
}
另,以下测试案例使用的gc配置:
-Xms10m -Xmx10m -XX:+PrintGC
1.1:强引用
平时我们用的默认就是强引用。所以也就没有一个类似于StrongReference
的类来代表强引用了。
1.2:软引用
使用类SoftReference代表一个软引用,比强引用稍微弱化些,在内存空间充足时发生GC不会被回收,但是在内存不足发生GC时将会被回收,所以适合用在类似于缓存这种并不会对程序起到决定性作用的场景中。如下例子:
public void softRefTest() {
/**
*
* @author mikechen
*/
Object obj = new Object();
SoftReference softRef = new SoftReference<Object>(obj);
obj = null;//删除强引用
byte[] b = new byte[1024 * 1024];
System.gc();//调用gc
System.out.println("gc之后的值:" + softRef.get()); // 对象依然存在
}
运行:
[GC (System.gc()) 2663K->1713K(9728K), 0.0011382 secs]
[Full GC (System.gc()) 1713K->1647K(9728K), 0.0055792 secs]
gc之后的值:java.lang.Object@330bedb4
Process finished with exit code 0
这里内存不足的场景我没有试出来,要么就OOM了。
软引用也可以选择和ReferenceQueue来一起使用,当软应用关联的对象被GC之后就会将软引用本身添加到队列中,如下:
public void softRefWithQueue() throws Exception {
ReferenceQueue<Object> queue = new ReferenceQueue<>();
Object obj = new Object();
SoftReference softRef = new SoftReference<Object>(obj, queue);//删除强引用
obj = null;
//调用gc
System.gc();
System.out.println("gc之后的值: " + softRef.get()); // 对象依然存在,虽然GC但内存足够,不会回收
//申请较大内存使内存空间使用率达到阈值,强迫gc
byte[] bytes = new byte[1024 * 1024 * 6];//如果obj被回收,则软引用会进入引用队列
System.out.println("111");
//调用gc
System.gc();
Reference<?> reference = queue.remove(); // 因为没有触发内存不足的场景,所以不会添加到队列中,所以这里会卡着
System.out.println("222");
if (reference != null) {
System.out.println("对象已被回收: " + reference.get());// 对象为null
}
}
运行:
同样试不出来内存不足被回收的场景。
1.3:弱引用
弱引用和软引用的区别是在发生GC时不管内存是否足够,都会被回收,看个例子:
private void weakRef() {
Object o1 = new Object();
WeakReference<Object> w1 = new WeakReference<Object>(o1);
// System.out.println(o1);
System.out.println(w1.get()); // 因为此时还有强引用,肯定不会被回收
o1 = null; // 手动去除强引用
System.gc();
// System.out.println(o1);
System.out.println("因为发生了GC,所以就被回收掉了:");
System.out.println(w1.get()); // 因为发生了GC,所以就被回收掉了
}
运行:
java.lang.Object@330bedb4
[GC (System.gc()) 1570K->745K(9728K), 0.0010741 secs]
[Full GC (System.gc()) 745K->622K(9728K), 0.0059361 secs]
因为发生了GC,所以就被回收掉了:
null
Process finished with exit code 0
当然也可以和ReferenceQueue一起使用,来监听
对象被回收的动作
:
private void weakRefWithQueueV1() throws Exception {
CC o1 = new CC();
o1.setName("张三");
ReferenceQueue referenceQueue = new ReferenceQueue();
Map<WeakReference, String> map = new HashMap<>();
WeakReference<CC> w1 = new WeakReference<CC>(o1, referenceQueue);
map.put(w1, w1.get().getName());
// System.out.println(w1);
//
// System.out.println(o1);
System.out.println(w1.get()); // 因为此时还有强引用,肯定不会被回收
o1 = null; // 手动去除强引用
System.gc();
// System.out.println(o1);
System.out.println("因为发生了GC,所以就被回收掉了:");
System.out.println(w1.get()); // 因为发生了GC,所以就被回收掉了
final Reference ref = referenceQueue.remove();
System.out.println(map.get(ref) + " 被回收了"); // 因为对象被回收,所以弱引用对象本身会被放到队列中
}
运行:
org.example.Main$CC@2503dbd3
[GC (System.gc()) 1647K->709K(9728K), 0.0009438 secs]
[Full GC (System.gc()) 709K->628K(9728K), 0.0058506 secs]
因为发生了GC,所以就被回收掉了:
null
张三 被回收了
Process finished with exit code 0
1.4:虚引用
虚引用是最弱的的一种引用,不决定对象的生命周期,有跟没有一样,即形同虚设
,必须和ReferenceQueue共同使用,一般用来监控jvm的gc活动,如下例子:
private void weakPhantomWithQueueV1() throws Exception {
CC o1 = new CC();
o1.setName("张三1");
ReferenceQueue referenceQueue = new ReferenceQueue();
Map<PhantomReference, String> map = new HashMap<>();
PhantomReference<CC> w1 = new PhantomReference<CC>(o1, referenceQueue);
map.put(w1, o1.getName());
// System.out.println(w1);
// System.out.println(o1);
System.out.println(w1.get()); // 因为此时还有强引用,肯定不会被回收
o1 = null; // 手动去除强引用
System.gc();
// System.out.println(o1);
System.out.println("因为发生了GC,所以就被回收掉了:");
System.out.println(w1.get()); // 因为发生了GC,所以就被回收掉了
final Reference ref = referenceQueue.remove();
System.out.println(map.get(ref) + " 被回收了"); // 因为对象被回收,所以弱引用对象本身会被放到队列中
}
运行:
null
[GC (System.gc()) 1643K->741K(9728K), 0.0012171 secs]
[Full GC (System.gc()) 741K->627K(9728K), 0.0062680 secs]
因为发生了GC,所以就被回收掉了:
null
张三1 被回收了
Process finished with exit code 0