zoukankan      html  css  js  c++  java
  • ThreadLocal源码分析:(三)remove()方法

    在ThreadLocal的get(),set()的时候都会清除线程ThreadLocalMap里所有key为null的value。 
    而ThreadLocal的remove()方法会先将Entry中对key的弱引用断开,设置为null,然后再清除对应的key为null的value。 
    本文分析remove方法

    系列文章链接:

    http://www.cnblogs.com/noodleprince/p/8657399.html

    http://www.cnblogs.com/noodleprince/p/8658333.html

    http://www.cnblogs.com/noodleprince/p/8659028.html

    ThreadLocal类的remove方法

    1 public void remove() {
    2     ThreadLocalMap m = getMap(Thread.currentThread());
    3     if (m != null)
    4         m.remove(this);     // 调用ThreadLocalMap的remove方法,见代码1
    5 }

    直接调用ThreadLocalMap的remove方法。

    代码1 
    ThreadLocalMap类的remove方法

     1 private void remove(ThreadLocal<?> key) {
     2     Entry[] tab = table;
     3     int len = tab.length;
     4     int i = key.threadLocalHashCode & (len-1);
     5     for (Entry e = tab[i];
     6          e != null;
     7          e = tab[i = nextIndex(i, len)]) {
     8         if (e.get() == key) {       // 考虑到可能的哈希冲突,一定要准确找到此key对应的entry
     9             e.clear();  // 调用Entry的clear方法,见代码2
    10             expungeStaleEntry(i);       // 又是这个清除key为null的entry的方法,见代码3
    11             return;
    12         }
    13     }
    14 }

    找到准确的key对应的entry之后,调用Entry的clear方法,紧接着调用expungeStaleEntry,对key为null的entry进行清理。

    代码2 
    Reference类的clear方法,EntryReference的子类

    1 public void clear() {
    2     this.referent = null;
    3 }

    很简单,就是把reference指向null,也就是将该entry的key指向null。方便后面对该entry进行清理。

    代码3 
    ThreadLocal.ThreadLocalMap类的expungeStaleEntry方法

     1 private int expungeStaleEntry(int staleSlot) {
     2     Entry[] tab = table;
     3     int len = tab.length;
     4 
     5     // expunge entry at staleSlot
     6     tab[staleSlot].value = null;
     7     tab[staleSlot] = null;
     8     size--;     // 以上代码,将entry的value赋值为null,这样方便GC时将真正value占用的内存给释放出来;将entry赋值为null,size减1,这样这个slot就又可以重新存放新的entry了
     9 
    10     // Rehash until we encounter null
    11     Entry e;
    12     int i;
    13     for (i = nextIndex(staleSlot, len); // 从staleSlot后一个index开始向后遍历,直到遇到为null的entry
    14          (e = tab[i]) != null;
    15          i = nextIndex(i, len)) {
    16         ThreadLocal<?> k = e.get();
    17         if (k == null) {    // 如果entry的key为null,则清除掉该entry
    18             e.value = null;
    19             tab[i] = null;
    20             size--;
    21         } else {
    22             int h = k.threadLocalHashCode & (len - 1);
    23             if (h != i) {   // key的hash值不等于目前的index,说明该entry是因为有哈希冲突导致向后移动到当前index位置的
    24                 tab[i] = null;
    25 
    26                 // Unlike Knuth 6.4 Algorithm R, we must scan until
    27                 // null because multiple entries could have been stale.
    28                 while (tab[h] != null)      // 对该entry,重新进行hash并解决冲突
    29                     h = nextIndex(h, len);
    30                 tab[h] = e;
    31             }
    32         }
    33     }
    34     return i;   // 返回经过整理后的,位于staleSlot位置后的第一个为null的entry的index值
    35 }

    expungeStaleEntry方法不止清理了staleSlot位置上的entry,还把staleSlot之后的key为null的entry都清理了,并且顺带将一些有哈希冲突的entry给填充回可用的index中。

    到这里ThreadLocalremove方法也分析完了。remove方法的关键就在于主动断开entry的key的引用链接,这样在后续的expungeStaleEntry方法中,就会将这种key为null的entry给设置为null,方便GC对内存进行回收。

    ThreadLocal的setgetremove方法看下来,除了正常的功能之外,就是多了对key为null的entry的清理工作,方便GC回收这部分占用的内存。expungeStaleEntry就是最核心的清理方法,这也是ThreadLocalMap的一种防范机制,因为ThreadLocalMap的生命周期和线程是一样长的,不采取这种防范机制,是会造成内存泄漏的。如果多定义了几个ThreadLocal对象,并且线程都将占用内存比较大的对象给放到对应的线程中,可能就会造成OOM异常了。 
    本文也是因为在网上看到有分析使用ThreadLocal造成的OOM异常,所以才深入看看ThreadLocal的源码。JDK源码写的确实很有水平,层次分明,功能封装粒度合适,自己看看还是感觉很通透的。

  • 相关阅读:
    欧几里得算法及扩展欧几里得(含)
    RP
    P1734_最大约数和
    The 2017 ACM-ICPC Asia East Continent League Final记录
    【数据结构】bzoj1651专用牛棚
    【数据结构】bzoj1455罗马游戏
    【数据结构】bzoj1636/bzoj1699排队
    【数据结构】bzoj3747Kinoman
    【计算几何】奇特的门
    Topcoder SRM 608 div1 题解
  • 原文地址:https://www.cnblogs.com/noodleprince/p/8659028.html
Copyright © 2011-2022 走看看