如果有什么错误的地方,希望指出。
前几天有个面试,在面试最后的时候,面试官说问个比较偏僻的知识点,问了关于Java引用的。于是我就把四种引用说了下。然后又问,你知道引用队列嘛?然后我懵逼了,只能说我不知道。
关于Java中的引用,可以看上面的链接,引用主要用于GC中的。
引用队列 ReferenceQueue 是用来配合引用工作的,没有 ReferenceQueue 一样可以运行。创建引用的时候可以指定关联的队列,当GC释放对象内存的时候,会将引用加入到引用队列的队列末尾,这相当于是一种通知机制。当关联的引用队列中有数据的时候,意味着引用指向的堆内存中的对象被回收。通过这种方式,JVM允许我们在对象被销毁后,做一些我们自己想做的事情。JVM提供了一个ReferenceHandler线程,将引用加入到注册的引用队列中。
关于引用队列,其类位于ref中,如图所示:
1 2 3 4 5 6 7 8 9 10 11
| ReferenceQueue<String> rq = newReferenceQueue<String>();
SoftReference<String> sr = newSoftReference<String>(new String("Soft"),rq);
WeakReference<String> wr = newWeakReference<String>(new String("Weak"),rq);
PhantomReference<String> pr = newPhantomReference<String>(new String("Phantom"),rq);
Reference<? extends String> ref = rq.poll();
|
ReferenceQueue 提供了三种方法来移除队列:
- poll():用于移除并返回该队列中的下一个引用对象,如果队列为空,则返回null
- remove():用于移除并返回该队列中的下一个引用对象,该方法会在队列返回可用引用对象之前一直阻塞
- remove (long timeout):用于移除并返回队列中的下一个引用对象。该方法会在队列返回可用引用对象之前一直阻塞,或者在超出指定超时后结束。如果超出指定超时,则返回null。如果指定超时为0,意味着将无限期地等待。
1 2 3 4 5 6 7 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
| public class ReferenceQueueDemo {
private static ReferenceQueue<byte[]> referenceQueue = new ReferenceQueue<>(); private static int _1M = 1024 * 1024;
public static void main(String[] args) {
Object object = new Object(); Map<Object, Object> map = new HashMap<>();
Thread thread = new Thread(() -> { try { int cnt = 0; WeakReference<byte[]> k; while ((k = (WeakReference) referenceQueue.remove()) != null) { System.out.println((cnt++) + "回收了:" + k); } } catch (InterruptedException e) { } }); thread.setDaemon(true); thread.start();
for (int i = 0; i < 10000; i++) { byte[] bytes = new byte[_1M]; WeakReference<byte[]> weakReference = new WeakReference<>(bytes, referenceQueue); map.put(weakReference, object); } System.out.println("map.size->" + map.size());
} }
|
因为map的key是WeakReference,所以在内存不足的时候,weakReference所指向的对象就会被GC,在对象被GC的同时,会把该对象的包装类即weakReference放入到ReferenceQueue里面。但是这个map的大小是10000。