四种引用类型分别对应了四种可达性:
当引用对象(软、弱、虚对象)的可达性发生变化的时候,它们就会被注册到 ReferenceQueue 里。
当一个软引用、弱引用被GC清理的时候( get()
会返回null),他们会被追加到引用队列中。所以软、弱引用配合队列使用一般用处不大。除非你做了一个软、弱引用的子类,并且在里面添加了一些属性:
ReferenceQueue<Foo> fooQueue = new ReferenceQueue<Foo>(); class ReferenceWithCleanup extends WeakReference<Foo> { Bar bar; ReferenceWithCleanup(Foo foo, Bar bar) { super(foo, fooQueue); this.bar = bar; } public void cleanUp() { bar.cleanUp(); } } public Thread cleanupThread = new Thread() { public void run() { while(true) { ReferenceWithCleanup ref = (ReferenceWithCleanup)fooQueue.remove(); ref.cleanUp(); } } }
当一个对象编程虚可达的时候,它的虚引用就会被添加到引用队列,当虚引用对象被清理或者虚引用对象变得不可达的时候,对象就才会变成不可达。
一般的引用
当要发生OOM之前,JVM会回收 SoftReference 所引用的对象。
软引用一般用来实现Cache,这样实现的Cache不会对内存造成压力,因为会被回收掉。
WeakReference 完全不影响垃圾回收,该回收的时候还是会被回收。
弱引用的特性用代码表示是:
Object referent3 = weakReference2.get(); if (referent3 != null) { // GC hasn't removed the instance yet } else { // GC has cleared the instance }
你可以用来实现,如果你不想保持对A对象的强引用,同时如果A对象没有被回收,那么我就可以干活,如果A对象被回收了,那我也无所谓的逻辑。
PhantomReference
完全不影响垃圾回收,该回收的时候还是会被回收。同时也无法得到所引用的对象, get()
方法永远返回null。虚引用一定要配合ReferenceQueue来使用:
Object counter = new Object(); ReferenceQueue refQueue = new ReferenceQueue<>(); PhantomReference<Object> p = new PhantomReference<>(counter, refQueue); counter = null; System.gc(); try { // Remove是一个阻塞方法,可以指定timeout,或者选择一直阻塞 Reference<Object> ref = refQueue.remove(1000L); if (ref != null) { // do something } } catch (InterruptedException e) { // Handle it }
前面可达性已经说了,“虚可达”后面的状态是“不可达”,那么一个对象变成“虚可达”的意思就是它现在已经被finalized,但是还没有被回收,配合ReferenceQueue,就能够得到“对象该清理的都清理了,都已经被finalized了,即将被回收”的事件。
`java.lang.ref.Reference#reachabilityFence(Object ref) 静态方法用来将ref所指向的对象设置为强引用。
这个是因为Java在垃圾回收的时候,会把下面的对象回收掉:
new Foo().action();
因为GC依赖的是可达性分析,而Foo对象实际上并不可达,就算它在调用方法,也是不可达的。为了避免在 action()
方法执行时被回收,可以添加这段代码:
public void action() { try { // do some work } finally { Reference.reachabilityFence(this); } }
这段代码的意思是在 action()
方法返回之前,Foo对象会保持强引用状态(虽然你会觉得代码位置不是应该放在第一行吗?但文档里是这么说的)。