强引用、软引用、弱引用、虚引用用法
- 强引用
- 弱引用
- 弱引用
- 虚引用
强引用
强引用是指程序中在程序代码之中类似“Object obj = new Object()”的引用关系,无论任何情况下,只要强引用关系还存在,垃圾回收器就不会回收掉被引用的对象。
强引用是我们最常用的引用,没什么好讲的,在集合源码中也可以见到将强引用置为null来使对象被回收的操作:
/*** HashMap中的清除方法,将所有节点遍历置为null* 使不可达对象在下次gc时被回收*/public void clear() {Node<K,V>[] tab;modCount++;if ((tab = table) != null && size > 0) {size = 0;for (int i = 0; i < tab.length; ++i)tab[i] = null;}}
弱引用
在虚拟机抛出OutOfMemoryError之前,所有对软可达对象的软引用都保证已被清除。也就是说在发生内存溢出之前会先清除软引用,并且软引用对象也会被清除。
这样我们就可以利用软引用做一层缓存,在空间充足的时候保存一些文件,在空间紧张时自动清理并且做一些善后操作。
public class test {public static void main(String[] args) {softReferenceOverHeadLimitResolve();}private static void softReferenceOverHeadLimitResolve() {int capacity = 1024 * 1024;//用来保存对象HashSet<SoftReference<SmallSoftObject>> set = new HashSet<>(capacity);// 引用队列,被清除的软引用会进入这个队列中,这个队列我们之后还会再用到ReferenceQueue<SmallSoftObject> referenceQueue = new ReferenceQueue<>();for (int i = 0; i < capacity; i++) {// 保存文件set.add(new SoftReference<>(new SmallSoftObject(), referenceQueue));// 如果之前保存的文件因为空间不足被清理,可以在这个方法里执行善后处理removeObject(set, referenceQueue);}System.out.println("End");}private static void removeObject(HashSet<SoftReference<SmallSoftObject>> set, ReferenceQueue<SmallSoftObject> referenceQueue) {//获得被清理的软引用,在set中删除Reference<? extends SmallSoftObject> poll = referenceQueue.poll();while (poll != null) {set.remove(poll);poll = referenceQueue.poll();}}static class SmallSoftObject {byte[] data = new byte[1024];}
}
现在我们添加运行参数,修改堆大小模拟内存不够的情况,并开启gc日志
-Xmx40m -XX:+PrintGC
以下是运行结果
弱引用
弱引用在下次垃圾回收发生时就会被回收。
WeakReference<byte[]> wk = new WeakReference<byte[]>(new byte[1024 * 1024 * 100]);System.out.println(wk.get());System.gc();System.out.println(wk.get());
虚引用
虚引用被回收后会进入引用队列等待,查看代码发现只有一个带队列的构造方法:
那么我们使用虚引用的目的就是当对象被回收,虚引用会进入引用队列,这是我们从引用队列取出引用后得知对象被回收的信息,进行验尸工作。
public class test {public static void main(String[] args) {testPhantomReference();}public static void testPhantomReference() {ReferenceQueue rq = new ReferenceQueue();byte[] bytes = new byte[1024 * 1024 * 100];PhantomReference<byte[]> pr = new PhantomReference<byte[]>(bytes, rq);//监控对象是否被清理Thread cleanUpThread = new Thread(() -> {try {while(true){Reference remove = rq.remove();System.out.println("对象被清理了" + remove);}} catch (InterruptedException e) {throw new RuntimeException(e);}});cleanUpThread.setDaemon(true);cleanUpThread.start();bytes = null;System.gc();}
这里我们启动一个守护线程去监控对象是否被清理,如果被清理则打印清理内容