前言:WeakHashMap可能平时使用的频率并不高,但是你可能听过WeakHashMap会进行自动回收吧,下面就对其原理进行分析。
注:本文jdk源码版本为jdk1.8.0_172
1.WeakHashMap介绍
WeakHashMap是一种弱引用的map,底层数据结构为数组+链表,内部的key存储为弱引用,在GC时如果key不存在强引用的情况下会被回收掉,而对于value的回收会在下一次操作map时回收掉,所以WeakHashMap适合缓存处理。
1 java.lang.Object 2 ↳ java.util.AbstractMap<K, V> 3 ↳ java.util.WeakHashMap<K, V> 4 5 public class WeakHashMap<K,V> 6 extends AbstractMap<K,V> 7 implements Map<K,V> {}
从WeakHashMap的继承关系上来看,可知其继承AbstractMap,实现了Map接口。其底层数据结构是Entry数组,Entry的数据结构如下:
从源码上可知,Entry的内部并没有存储key的值,而是通过调用父类的构造方法,传入key和ReferenceQueue,最终key和queue会关联到Reference中,这里是GC时,清清除key的关键,这里大致看下Reference的源码:
1 private static class ReferenceHandler extends Thread { 2 3 private static void ensureClassInitialized(Class<?> clazz) { 4 try { 5 Class.forName(clazz.getName(), true, clazz.getClassLoader()); 6 } catch (ClassNotFoundException e) { 7 throw (Error) new NoClassDefFoundError(e.getMessage()).initCause(e); 8 } 9 } 10 11 static { 12 // pre-load and initialize InterruptedException and Cleaner classes 13 // so that we don't get into trouble later in the run loop if there's 14 // memory shortage while loading/initializing them lazily. 15 ensureClassInitialized(InterruptedException.class); 16 ensureClassInitialized(Cleaner.class); 17 } 18 19 ReferenceHandler(ThreadGroup g, String name) { 20 super(g, name); 21 } 22 23 public void run() { 24 // 注意这里为一个死循环 25 while (true) { 26 tryHandlePending(true); 27 } 28 } 29 } 30 static boolean tryHandlePending(boolean waitForNotify) { 31 Reference<Object> r; 32 Cleaner c; 33 try { 34 synchronized (lock) { 35 if (pending != null) { 36 r = pending; 37 // 'instanceof' might throw OutOfMemoryError sometimes 38 // so do this before un-linking 'r' from the 'pending' chain... 39 c = r instanceof Cleaner ? (Cleaner) r : null; 40 // unlink 'r' from 'pending' chain 41 pending = r.discovered; 42 r.discovered = null; 43 } else { 44 // The waiting on the lock may cause an OutOfMemoryError 45 // because it may try to allocate exception objects. 46 if (waitForNotify) { 47 lock.wait(); 48 } 49 // retry if waited 50 return waitForNotify; 51 } 52 } 53 } catch (OutOfMemoryError x) { 54 // Give other threads CPU time so they hopefully drop some live references 55 // and GC reclaims some space. 56 // Also prevent CPU intensive spinning in case 'r instanceof Cleaner' above 57 // persistently throws OOME for some time... 58 Thread.yield(); 59 // retry 60 return true; 61 } catch (InterruptedException x) { 62 // retry 63 return true; 64 } 65 66 // Fast path for cleaners 67 if (c != null) { 68 c.clean(); 69 return true; 70 } 71 // 加入对列 72 ReferenceQueue<? super Object> q = r.queue; 73 if (q != ReferenceQueue.NULL) q.enqueue(r); 74 return true; 75 } 76 77 static { 78 ThreadGroup tg = Thread.currentThread().getThreadGroup(); 79 for (ThreadGroup tgn = tg; 80 tgn != null; 81 tg = tgn, tgn = tg.getParent()); 82 // 创建handler 83 Thread handler = new ReferenceHandler(tg, "Reference Handler"); 84 /* If there were a special system-only priority greater than 85 * MAX_PRIORITY, it would be used here 86 */ 87 // 线程优先级最大 88 handler.setPriority(Thread.MAX_PRIORITY); 89 // 设置为守护线程 90 handler.setDaemon(true); 91 handler.start(); 92 93 // provide access in SharedSecrets 94 SharedSecrets.setJavaLangRefAccess(new JavaLangRefAccess() { 95 @Override 96 public boolean tryHandlePendingReference() { 97 return tryHandlePending(false); 98 } 99 }); 100 }