强引用
强引用是指程序中在程序代码之中类似“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();
}
这里我们启动一个守护线程去监控对象是否被清理,如果被清理则打印清理内容