垃圾收集过程当中,对象的可触及状态改变的时候,能够把引用对象和引用队列关联起来【这里说的关联,是说垃圾收集器会把要回收的对象添加到引用队列ReferenceQueue】,这样在可触及性发生变化的时候获得“通知”。html
当垃圾收集器对加入队列的对象改变可触及性的时候,就能够收到异步通知了。java
看下面的代码:数组
1缓存 2app 3异步 4ide 5函数 6测试 7flex 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
|
咱们用一个线程检测referenceQueue里面是否是有内容,若是有内容,打印出来queue里面的内容。
从这个例子中,咱们能够看出来,虚引用的做用是,咱们能够声明虚引用来引用咱们感兴趣的对象,在gc要回收的时候,gc收集器会把这个对象添加到referenceQueue,这样咱们若是检测到referenceQueue中有咱们感兴趣的对象的时候,说明gc将要回收这个对象了。此时咱们能够在gc回收以前作一些其余事情,好比记录些日志什么的。
———————————————-分割—————————————————-
感谢蓝大牛分享下面的例子。
在java中,finalize函数原本是设计用来在对象被回收的时候来作一些操做的(相似C++的析构函数)。可是对象被GC何时回收的时间,倒是不固定的,这样finalize函数很尴尬。虚引用能够用来解决这个问题。
在建立虚引用的时候必须传入一个引用队列。在一个对象的finalize函数被调用以后,这个对象的幽灵引用会被加入到引用队列中。经过检查队列的内容就知道对象是否是要准备被回收了。
幽灵引用的使用并很少见,主要是实现细粒度的内存控制。好比下面代码实现一个缓存。程序在确认原来的对象要被回收以后,才申请内存建立新的缓存。
在上面的代码中,每次申请新的缓存的时候,都要确保以前的字节数组被成功回收。引用队列的remove方法会阻塞直到虚引用被加入到引用队列中。【只有对象在内存中被移除以后才会进入引用队列中】【?这里有点不太肯定。后续补发】
不过注意,这种方式可能会致使gc次数过多,程序吞吐量降低。
在 jdk 1.2 及其之后,引入了强引用、软引用、弱引用、虚引用这四个概念。网上不少关于这四个概念的解释,但大可能是概念性的泛泛而谈,今天我结合着代码分析了一下,首先咱们先来看定义与大概解释(引用类型在包 java.lang.ref 里)。
一、强引用(StrongReference)
强引用不会被GC回收,而且在java.lang.ref里也没有实际的对应类型。举个例子来讲:
Object obj = new Object();
这里的obj引用即是一个强引用,不会被GC回收。
二、软引用(SoftReference)
软引用在JVM报告内存不足的时候才会被GC回收,不然不会回收,正是因为这种特性软引用在caching和pooling中用处普遍。软引用的用法:
1 2 3 4 |
|
三、弱引用(WeakReference)
当GC一但发现了弱引用对象,将会释放WeakReference所引用的对象。弱引用使用方法与软引用相似,但回收策略不一样。
四、虚引用(PhantomReference)
当GC一但发现了虚引用对象,将会将PhantomReference对象插入ReferenceQueue队列,而此时PhantomReference所指向的对象并无被GC回收,而是要等到ReferenceQueue被你真正的处理后才会被回收。虚引用的用法:
1 2 3 4 5 6 7 8 9 |
|
看了简单的定义以后,咱们结合着代码来测试一下,强引用就不用说了,软引用的描述也很清楚,关键是 “弱引用” 与 “虚引用”。
弱引用:
1 2 3 4 5 6 7 8 9 10 11 |
|
因为System.gc()是告诉JVM这是一个执行GC的好时机,但具体执不执行由JVM决定,所以当JVM决定执行GC,获得的结果即是(事实上这段代码通常都会执行GC):
java.lang.Object@de6ced
null
null
java.lang.ref.WeakReference@1fb8ee3
从执行结果得知,经过调用weakRef.get()咱们获得了obj对象,因为没有执行GC,所以refQueue.poll()返回的null,当咱们把obj = null;此时没有引用指向堆中的obj对象了,这里JVM执行了一次GC,咱们经过weakRef.get()发现返回了null,而refQueue.poll()返回了WeakReference对象,所以JVM在对obj进行了回收以后,才将weakRef插入到refQueue队列中。
虚引用:
1 2 3 4 5 6 7 8 9 10 11 |
|
一样,当JVM执行了GC,获得的结果即是:
null
null
null
java.lang.ref.PhantomReference@1fb8ee3
从执行结果得知,咱们先前说的没有错,phanRef.get()无论在什么状况下,都会返回null,而当JVM执行GC发现虚引用以后,JVM并无回收obj,而是将PhantomReference对象插入到对应的虚引用队列refQueue中,当调用refQueue.poll()返回PhantomReference对象时,poll方法会先把PhantomReference的持有队列queue(ReferenceQueue<? super T>)置为NULL,NULL对象继承自ReferenceQueue,将enqueue(Reference paramReference)方法覆盖为return false,而此时obj再次被GC发现时,JVM再将PhantomReference插入到NULL队列中便会插入失败返回false,此时GC便会回收obj。事实上经过这段代码咱们也的却看不出来obj是否被回收,但经过 PhantomReference 的javadoc注释中有一句是这样写的:
Once the garbage collector decides that an object obj
is phantom-reachable, it is being enqueued on the corresponding queue, but its referent is not cleared. That is, the reference queue of the phantom reference must explicitly be processed by some application code.
翻译一下(这句话很简单,我相信不少人应该也看得懂):
一旦GC决定一个“obj”是虚可达的,它(指PhantomReference)将会被入队到对应的队列,可是它的指代并无被清除。也就是说,虚引用的引用队列必定要明确地被一些应用程序代码所处理。
弱引用与虚引用的用处
软引用很明显能够用来制做caching和pooling,而弱引用与虚引用呢?其实用处也很大,首先咱们来看看弱引用,举个例子:
1 2 3 4 5 6 7 |
|
全部我添加进 registeredObjects 中的object永远不会被GC回收,由于这里有个强引用保存在registeredObjects里,另外一方面若是我把代码改成以下:
1 2 3 4 5 6 7 |
|
如今若是GC想要回收registeredObjects中的object,便可以实现了,一样在使用HashMap若是想实现如上的效果,一种更好的实现是使用WeakHashMap。
而虚引用呢?咱们先来看看javadoc的部分说明:
Phantom references are useful for implementing cleanup operations that are necessary before an object gets garbage-collected. They are sometimes more flexible than the finalize()
method.
翻译一下:
虚引用在实现一个对象被回收以前必须作清理操做是颇有用的。有时候,他们比finalize()方法更灵活。
很明显的,虚引用能够用来作对象被回收以前的清理工做。