不同的引用类型,主要体现的是对象不同的可达性(reachable)状态和对垃圾收集的影响。
强引用:只要有强引用指向一个对象,就表明对象还活着,垃圾收集器就不会回收。
一个普通对象,如果没有其他引用关系,只要超过了引用作用域或显式的将强引用设为null,就会被垃圾回收。
软引用: 当jvm内存不足的时候才会回收软引用指向的对象。如果还有空闲内存,就会保留,实现了缓存的作用。
弱引用: 一定会被垃圾收集,它仅仅提供了一种访问在弱引用状态下对象的途经。
如果获取对象时还在就使用它,否则重新实例化。同样用来实现缓存。
虚引用(幻象引用):不能通过他来访问对象,提供了一种通过对象被finalize后做一些事的机制。利用幻象引用监控对象的创建与销毁。例如netty的对外内存的释放。
知识拓展
1、对象可达性状态流转分析
通过各自的引用访问对象就是各自的可达性。
所有引用 类型都是抽象类java.lang.ref.Reference的子类
幻象引用get方法获取一直返回null,其他引用如果对象还没销毁,可以通过get方法获取原有对象,所以可以通过访问到的对象来重新改变引用关系,从而人为的改变对象的可达性状态。
内存泄漏:
错误的保持了强引用,弱引用指向了这个强引用,那么就无法回到弱可达的状态了,就会产生内存泄露。
2、引用队列的使用
幻象引用 可利用引用队列 在finalize后执行后续处理逻辑。
Object counter = new Object();
ReferenceQueue refQueue = new ReferenceQueue<>();
PhantomReference<Object> p = new PhantomReference<>(counter, refQueue);
counter = null;
System.gc();
try {
// Remove 是一个阻塞方法,可以指定 timeout,或者选择一直阻塞
Reference<Object> ref = refQueue.remove(1000L);
if (ref != null) {
// do something
}
} catch (InterruptedException e) {
// Handle it
}
3、Reachability Fence 可达性屏障
如果对象本身并没有强引用,但它的部分属性还在使用,就需要一个方法通知JVM对象是在被使用的。
class Resource {
private static ExternalResource[] externalResourceArray = ...
int myIndex; Resource(...) {
myIndex = ...
externalResourceArray[myIndex] = ...;
...
}
protected void finalize() {
externalResourceArray[myIndex] = null;
...
}
public void action() {
try {
// 需要被保护的代码 执行finalize就不会回收该对象
int i = myIndex;
Resource.update(externalResourceArray[i]);
} finally {
// 调用 reachbilityFence,明确保障对象 strongly reachable
Reference.reachabilityFence(this);
}
}
private static void update(ExternalResource ext) {
ext.status = ...;
}
}