基于JDK1.8 rt.jar是java中的基础类库,在它的 java.lang.ref包下,有着一堆关于引用的类。软引用、弱引用以及虚引用的定义就在其中。另外还有一个FinalReference。
* ReferenceQueue//引用队列
* Reference(abstract)//抽象类
* SoftReference//软引用
* WeakReference//弱引用
* PhantomReference//虚引用
* FinalReference
*Finalizer
*FinalizerHistogram
jdk1.8的包中并没有这个类,是普通类的引用。在Hotspot的垃圾回收过程中,采用可达性分析算法判定对象是否存活,可以到达GC Roots的对象是永远不会被当做垃圾回收的;
软引用常常被用于实现一些高速且敏感的缓存,如Guava中的LocalCache中就是用了SoftReference来做缓存 它的特点是:如果内存空间足够,垃圾回收器就不会回收它,如果内存空间不足了,就会回收这些对象的内存。
弱引用同样经常被用做缓存的实现,在JDK中比较常见的是维护一种非强制性的映射关系的实现,动态代理的缓存就是基于它实现的。 它的特点是:在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。可见它相对于软引用来说,拥有更为短暂的生命周期。
如果一个对象只被一个PhantomReference所引用,那等于没有任何引用。它随时都可以 回收。虚引用经常被用作 跟踪对象被垃圾回收的活动。
以上三种引用类型,均可以和ReferenceQueue引用队列一起使用,例如:
ReferenceQueue referenceQueue = new ReferenceQueue();//引用队列
Object obj = new Object();//强引用
WeakReference weakReference = new WeakReference(obj,referenceQueue);//初始化弱引用,PhantomReference只有这一个构造函数
一个对象,通过被“弱引用对象”引用,而改变了它的生命周期。可以方便它更快的被垃圾回收。试想在一个map构造的缓存系统中,缓存值是一直被map强引用着的,map值一直被使用,所有的缓存值也会一直得不到释放,这是很危险的,随时都会带来OOM的危险,这时,Reference就派上用场了:通过一个软引用或者虚引用隔离开map对象和缓存值的直接引用,变成map->reference->缓存值,一旦缓存值没有强引用,就可以很快被回收了。
public class Oil {
private String name;
public Oil(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("Oil "+name+" finalize.");
}
@Override
public String toString() {
return "Oil "+name+" print!";
}
}
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
public class ReferenceQueueTest {
public static void main(String[] args) throws InterruptedException {
ReferenceQueue<Oil> oilReferenceQueue = new ReferenceQueue<>();
Map map = new HashMap();
Oil oil = new Oil("花生油");
Oil oil1 = new Oil("葵花籽油");
WeakReference<Oil> oilWeakReference = new WeakReference<>(oil, oilReferenceQueue);
WeakReference<Oil> oilWeakReference1 = new WeakReference<>(oil1);
map.put(oilReferenceQueue,oilWeakReference1);
while (true) {
System.out.println(oilWeakReference);
System.out.println(oilWeakReference1);
System.out.println(oilWeakReference.get());
System.out.println(oilWeakReference1.get());
Reference<? extends Oil> reference = null;
while ((reference = oilReferenceQueue.poll()) != null) {
System.out.println("ReferenceQueue内:" + reference);
map.remove(reference);
return;
}
oil = null;
System.out.println("=========gc===========");
System.gc();//调用GC
TimeUnit.SECONDS.sleep(10);
}
}
}
打印结果:
"C:\Program Files\Java\jdk1.8.0_221\bin\java.exe" **-XX:+PrintGCDetails** "...
java.lang.ref.WeakReference@2503dbd3
java.lang.ref.WeakReference@4b67cf4d
Oil 花生油 print!
Oil 葵花籽油 print!
=========gc===========
[GC (System.gc()) [PSYoungGen: 3334K->744K(38400K)] 3334K->752K(125952K), 0.0008666 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
[Full GC (System.gc()) [PSYoungGen: 744K->0K(38400K)] [ParOldGen: 8K->647K(87552K)] 752K->647K(125952K), [Metaspace: 3228K->3228K(1056768K)], 0.0049257 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
Oil 花生油 finalize.
java.lang.ref.WeakReference@2503dbd3
java.lang.ref.WeakReference@4b67cf4d
null
Oil 葵花籽油 print!
ReferenceQueue内:java.lang.ref.WeakReference@2503dbd3
Heap
PSYoungGen total 38400K, used 3661K [0x00000000d5e00000, 0x00000000d8880000, 0x0000000100000000)
eden space 33280K, 11% used [0x00000000d5e00000,0x00000000d6193500,0x00000000d7e80000)
from space 5120K, 0% used [0x00000000d7e80000,0x00000000d7e80000,0x00000000d8380000)
to space 5120K, 0% used [0x00000000d8380000,0x00000000d8380000,0x00000000d8880000)
ParOldGen total 87552K, used 647K [0x0000000081a00000, 0x0000000086f80000, 0x00000000d5e00000)
object space 87552K, 0% used [0x0000000081a00000,0x0000000081aa1e00,0x0000000086f80000)
Metaspace used 3770K, capacity 4540K, committed 4864K, reserved 1056768K
class space used 416K, capacity 428K, committed 512K, reserved 1048576K
Process finished with exit code 0
如果缓存值被回收了,那么引用对象(也就是WeakReference或者SoftReference的对象)也就没用了,失去被应用对象的它们,会被放入ReferenceQueue中,方便更快回收。JDK中常见的回收代码如下:
private void expungeStaleEntries() {
CacheKey<K> cacheKey;
while ((cacheKey = (CacheKey<K>)refQueue.poll()) != null) {//从队列拉出没用的引用类型对象
cacheKey.expungeFrom(map, reverseMap);//将它们从map中剔除,方便被垃圾回收
}
}
FinalReference和Finalizer以及常见的finnalize()方法应该放在一起说。
static {
ThreadGroup tg = Thread.currentThread().getThreadGroup();
for (ThreadGroup tgn = tg;
tgn != null;
tg = tgn, tgn = tg.getParent());
Thread finalizer = new FinalizerThread(tg);
finalizer.setPriority(Thread.MAX_PRIORITY - 2);
finalizer.setDaemon(true);
finalizer.start();
}
private static class FinalizerThread extends Thread {
private volatile boolean running;
FinalizerThread(ThreadGroup g) {
super(g, "Finalizer");
}
public void run() {
// in case of recursive call to run()
if (running)
return;
// Finalizer thread starts before System.initializeSystemClass
// is called. Wait until JavaLangAccess is available
while (!VM.isBooted()) {
// delay until VM completes initialization
try {
VM.awaitBooted();
} catch (InterruptedException x) {
// ignore and continue
}
}
final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
running = true;
for (;;) {
try {
Finalizer f = (Finalizer)queue.remove();//从引用队列取出Finalizer类
f.runFinalizer(jla);// 从Finalizer的引用中剥离对象,并且调用该对象的finalize()方法
} catch (InterruptedException x) {
// ignore and continue
}
}
}
}
它实现了一个守护线程,如注释所示,它将整个处理finalize的处理过程串联起来了。但是由于优先级并不是最高,所以当CPU资源不充足的时候,它的执行就会收到影响,也就不能保证你的连接一定被释放了。在《java编程思想》中,作者曾经写到:如果希望进行除释放存储空间之外的清理工作,还是得明确调用某个恰当的Java方法。这就等同与使用析构函数了,只是没有它方便。