JDK ThreadLocal resolve

Java ThreadLocal resolve

ThreadLocal thread local variables, private thread in the Thread class with ThreadLocal.ThreadLocalMap threadLocalsstorage in the form of an array. Because thread private variables, so it will not multithreaded access security thread, here it began parsing (JDK1.8 of ThreadLocal ).

threadLocalHashCode

Before using ThreadLocal to say, first say a more important thing is threadLocalHashCode, this value is used to calculate the hashCode 当前 ThreadLocal 应当位于 ThreadLocalMap 的数组中的下标, why should it, please read

    // ThreadLocalHashCode 递增值
    private static final int HASH_INCREMENT = 0x61c88647;

  /**
   * Returns the next hash code.
   */
  private static int nextHashCode() {
    return nextHashCode.getAndAdd(HASH_INCREMENT);
  }
    // 每个 ThreadLocal 的 threadLocalHashCode 通过一个静态的原子变量递增而获得
    private final int threadLocalHashCode = nextHashCode();


ThreadLocal#set 方法

May store a plurality ThreadLocalMap ThreadLocal variable, when the access key into the Entry ThreadLocal as in ThreadLocalMap 存储数据的对象为 Entry[], with the key index calculation belongs int i = key.threadLocalHashCode & (len-1)calculation, if the existing index data, according to key (ThreadLocal variable) to determine whether the ThreadLocal, ThreadLocal press is not the same sequence as in the future to find empty ( 或者 ThreadLocal 已经被GC回收) value is provided below Entry is setimportant part of the source code for the method:

    // 最开始计算的下标i
    int i = key.threadLocalHashCode & (len-1);
    // 从计算出来的数组中的第 i 个开始往后找, i > len-1 的时候就从0开始
  for (Entry e = tab[i];
       e != null;
       e = tab[i = nextIndex(i, len)]) {
    // nextIndex(i,len): ((i + 1 < len) ? i + 1 : 0); i+1>=len则从下标0开始
    ThreadLocal<?> k = e.get();
    // 根据 key 判断是否为当前的 ThreadLocal
    if (k == key) {
      e.value = value;
      return;
    }
    // Entry 的 key 为空, 但是上面循环的判断是 Entry 不为空, 表明作为 Entry 的 key的 ThreadLocal 已经被 GC 回收, 所以此时设置新的 key(ThreadLocal) 和 value.
    if (k == null) {
      // 替换掉旧的 ThreadLocal
      replaceStaleEntry(key, value, i);
      return;
    }
    // 已经找到为空的 Entry, 直接 new 一个 Entry
    tab[i] = new Entry(key, value);
    int sz = ++size;
    if (!cleanSomeSlots(i, sz) && sz >= threshold)
      // 扩容将所有的 Entry 重新设置
      rehash();
  }


ThreadLocal#get 方法

get with the method set method still has a lot at the same, look at the code

    private Entry getEntry(ThreadLocal<?> key) {
    // 计算下标
    int i = key.threadLocalHashCode & (table.length - 1);
    Entry e = table[i];
    // 如果对应下标的 Entry 属于当前 ThreadLocal 的话, 直接返回结果
    if (e != null && e.get() == key)
      return e;
    else
      // 会到这里的原因是因为 set 的时候的 hash 冲突, 冲突后会往后查找(超过len-1则从0开始)可以放置的位置, 所以前面初始化 hashCode 的时候是用的应当位于的位置
      return getEntryAfterMiss(key, i, e);
    }
    // e 为应当属于它的 Entry
    private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
    Entry[] tab = table;
    int len = tab.length;
    // 
    while (e != null) {
      ThreadLocal<?> k = e.get();
      // 查找到属于自己的 value
      if (k == key)
        return e;
      // 如果在查找的时候发现有的 Entry 的 key(ThreadLocal) 已经被 GC 回收, 则进行回收
      if (k == null)
        expungeStaleEntry(i);
      else
        // 往后查找, 超过 len-1 则从 0 开始继续
        i = nextIndex(i, len);
      e = tab[i];
    }
    return null;
  }


WeakReference on the ThreadLocal

In saying ThreadLocal 的 WeakReferencebefore, briefly explain WeakReference.

WeakReference

Weak reference object is different from java with new creation (strong reference), and when the object is only weak references (weakReference), the time for GC, it will be recycled (not the object of strong references will be recovered gc)

    Object o = new Object();
  WeakReference weakReference = new WeakReference(o);
  System.out.println("beforeGC: "+weakReference.get());
  o = null;
  System.gc();
  System.out.println("afterGC: "+weakReference.get());

  Object o2 = new Object();
  WeakReference weakReference2 = new WeakReference(o2);
  System.out.println("beforeGC2: "+weakReference2.get());
  System.gc();
  System.out.println("afterGC2: "+weakReference2.get());

The output is:

beforeGC: java.lang.Object@5e2de80c
afterGC: null
beforeGC: java.lang.Object@1d44bcfa
afterGC: java.lang.Object@1d44bcfa

As can be seen from this, even if there weakFerence weak references, it can not prevent the object from being recovered GC off (because it is weak ah ha).


WeakReference ThreadLocal used

See the above line from the method set tab[i] = new Entry(key, value)

Entry is a static class

    // Entry 继承 WeakReference, 多了个 value 记录 ThreadLocal 的值
    static class Entry extends WeakReference<ThreadLocal<?>> {
        // 这个值就是 ThreadLocal set 进来的值
    Object value;
    Entry(ThreadLocal<?> k, Object v) {
      // 将 ThreadLocal 设置为引用对象
      super(k);
      value = v;
    }
  }

super (k) is set when the ThreadLocal object reference, access, call the get method of the parent class, entry.get () -> return is the object reference (this is ThreadLocal)

    // super(k)
    Reference(T referent) {
    this(referent, null);
  }
  // set reference
  Reference(T referent, ReferenceQueue<? super T> queue) {
    this.referent = referent;
    this.queue = (queue == null) ? ReferenceQueue.NULL : queue;
  }
    // get
    public T get() {
    return this.referent;
    }

The code looks difficult, said the following about the JDK ThreadLocal cause problems with memory leaks.

In the above methods set and get in, there is such (similar) piece of code:

    for (Entry e = tab[i];
       e != null;
       e = tab[i = nextIndex(i, len)]) {
    // nextIndex(i,len): ((i + 1 < len) ? i + 1 : 0); i+1>=len则从下标0开始
    ThreadLocal<?> k = e.get();
    // 根据 key 判断是否为当前的 ThreadLocal
    if (k == key) {
      e.value = value;
      return;
    }
    // Entry 的 key 为空, 但是上面循环的判断是 Entry 不为空, 表明作为 Entry 的 key的 ThreadLocal 已经被 GC 回收, 所以此时设置新的 key(ThreadLocal) 和 value.
    if (k == null) {
      // 替换掉旧的 ThreadLocal
      replaceStaleEntry(key, value, i);
      return;
    }

if(k == null)Here, together with the outer loop is if(e!=null && k == null), Entry value is set too ThreadLocal before the show is not empty, but k(key)==nullthat is ThreadLocal == nullwhy ThreadLocal be empty? Speaking in front of us Entry 里面对 ThreadLocal(key)is used 弱引用, that is Entry 持有 ThreadLocal 的引用并不会阻止GC 将 ThreadLocal 回收掉, if ThreadLocal manually set to null, then lost a strong reference, if other places do not have this strong references to the ThreadLocal, ThreadLocal this GC will be recycled out, this time Entry inside the key == null, but on the value Entry is strong references, so the value will not be recovered off the GC , this time causing the Entry of key == null but value! = null, if no other ThreadLocal is set into (or get) the same Entry, then 这个 Entry 将会一直持有无用的 value, 造成内存泄漏, 泄漏的过多最终会导致 OOM.

It is in solution before ThreadLocal set to null, first call the remove method:

    private void remove(ThreadLocal<?> key) {
    Entry[] tab = table;
    int len = tab.length;
    int i = key.threadLocalHashCode & (len-1);
    for (Entry e = tab[i];
         e != null;
         e = tab[i = nextIndex(i, len)]) {
      // 找到与自己匹配的 Entry
      if (e.get() == key) {
        e.clear();
        // 这个方法名也说的很清楚了, 删掉陈旧的 Entry
        expungeStaleEntry(i);
        return;
      }
    }
  }

So use the JDK ThreadLocal, when not needed to manually call remove () method to prevent lead to memory leaks, resulting OOM.

Guess you like

Origin www.cnblogs.com/wuhaonan/p/11427119.html