目录
概述
Java中的引用关系可以分为强引用,软引用(SoftReference)、弱引用(WeakReference)和虚引用(PhantomReference);引用与对象以及GC Root直接的关系如下图所示:
强引用
强引用就是普通的赋值语句,只要GC Root到对象之间存在引用关系,改对象就不会被回收
软引用
软引用要弱于强引用,在GC Root 到引用对象之间还存在可达关系,只要JVM认为存在OOM风险就可以被回收
典型使用场景
1.缓存,被回收不影响系统可用性;比如在JDK反射中使用较多
弱引用
弱引用关系要比软引用之间的关系更弱,只要发生GC就会被回收,不管内存空间是否充足;也可以配合引用队列使用
典型使用场景
1.缓存,比如WeakHashMap, ThreadLocal等
虚引用
虚引用是最特殊的一个引用,虚引用的简单来讲就是形同虚设,无法从引用中获取引用对象,既然这样,虚引用的作用是什么呢?它主要用来跟踪对象被垃圾回收的活动,当垃圾
回收器准备回收这个对象时,如果发现它有虚引用就会将这个虚引用添加到与之关联的引用队列中。
典型使用场景
对象回收时,清理关联资源,比如Cleaner,以及堆外缓存管理等。
场景验证
为了验证是引用关系是否如上文所述,特准备如下代码:
public class 四种引用 {
public static void main(String[] args) throws InterruptedException {
//强引用
Object strongReference = new Object();
//引用队列
ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
/**
* 软引用
* 如果仅有软引用对象时,首次垃圾回收不会回收改对象,再次回收时才会回收改对象
* 软引用可以配合引用队列使用
*/
SoftReference<Object> softReference = new SoftReference<>(new Object(), referenceQueue);
//reference handler
new Thread(() -> {
try {
Reference<? extends Object> reference = null;
while ((reference = referenceQueue.remove()) != null) {
if (reference instanceof SoftReference) {
System.out.println("软引用引用对象被回收");
}
if (reference instanceof PhantomReference) {
System.out.println("虚引用引用对象被回收");
}
if (reference instanceof WeakReference) {
System.out.println("弱引用引用对象被回收");
}
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}).start();
/**
* 如果仅有弱引用,只要发生垃圾回收就会回收改对象
*/
WeakReference<Object> weakReference = new WeakReference<>(new Object(), referenceQueue);
System.out.println("弱引用:" + weakReference.get());
//手动执行GC,
System.gc();
// 手动执行GC 后 弱引用被引用的对象已经被回收了,此时get到的值为null
System.out.println("弱引用:" + weakReference.get());
/**
* 必须配合引用队列使用,当虚引用引用的对象回收时,会将徐引用对象入队,由Referen Handler线程释放其关联的外部资源
*/
PhantomReference<Object> phantomReference = new PhantomReference<>(new Object(), referenceQueue);
try {
SoftReference<HashMap> holder = new SoftReference<>(new HashMap<>());
for (int i = 0; i < 1000; i++) {
HashMap hashMap = holder.get();
Thread.sleep(20);
if (Objects.nonNull(hashMap)) {
hashMap.put(i, new Object[1024]);
}
if (softReference.get() == null) {
System.out.println("强引用: " + strongReference);
System.out.println("软引用:" + softReference.get());
break;
}
}
} catch (Throwable e) {
System.out.println("OutOfMemoryError后:" + softReference.get());
}
System.out.println("强引用: " + strongReference);
System.out.println("软引用:" + softReference.get());
System.out.println("弱引用:" + weakReference.get());
System.out.println("虚引用 " + phantomReference.get());
TimeUnit.SECONDS.sleep(20);
System.exit(-1);
}
}
jvm 参数:-Xms3m -Xmx3m 执行结果如下:
弱引用:java.lang.Object@5ca881b5
[0.087s][info ][gc,start ] GC(1) Pause Full (System.gc())
弱引用引用对象被回收
弱引用:null
[1.193s][info ][gc,ergo ] Attempting maximum full compaction clearing soft references
软引用引用对象被回收
虚引用引用对象被回收
强引用: java.lang.Object@24d46ca6
软引用:null
强引用: java.lang.Object@24d46ca6
软引用:null
弱引用:null
虚引用 null
Cleaner
在程序执行过程中创建的对象将由垃圾收集器 (GC)自动删除。当某个对象未被任何线程引用时,并且当JVM确定无法访问该对象时,则可以进行垃圾回收。
Object类具有finalize() 方法,在尝试从堆中删除对象之前,GC 将自动调用该方法。在Java 9中,已经不建议使用finalize()方法,并将新类java.lang.ref.Cleaner 添加到垃圾回收管理中。当对象可以进行垃圾回收时,Cleaner 类的对象会自动得到通知。需要通过使用register()方法向清理器对象注册正在垃圾回收的对象
示例代码
public class CleanerTest {
public static void test() {
Object instance = new Object();
Cleaner cleaner = Cleaner.create();
cleaner.register(instance, () -> {
System.out.println("do cleaner");
});
}
public static void main(String[] args) {
System.out.println("Cleaner Test");
test();
System.gc();
}
}
输出结果
Cleaner Test
do cleaner