前言: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的数据结构如下:

WeakHashMap源码分析

从源码上可知,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     }
View Code

相关文章: