ThreadLocal内存泄漏

  • ThreadLocal内存泄漏是指由于ThreadLocal的使用不当,导致线程结束后其关联的ThreadLocal变量无法被垃圾回收,从而引发内存泄漏问题。

1. 内存泄漏原因

  • ThreadLocal内存泄漏的根本原因在于其内部实现机制

  • ThreadLocalMapEntry定义如下:

    1
    2
    3
    4
    5
    6
    7
    8
    static class Entry extends WeakReference<ThreadLocal<?>> {
    Object value;

    Entry(ThreadLocal<?> k, Object v) {
    super(k);
    value = v;
    }
    }
  • 其中:

    • key(ThreadLocal 对象)是“弱引用”,也就是把ThreadLocal对象给丢了,JVM下轮GC就能把key给回收掉。
    • value是“强引用”,也就是只要Thread对象还在,value就不会被回收掉。
  • key被GC回收掉后,Map里会出现keynull,但value不为null的情况,理想情况下,下次访问ThreadLocalset/remove时,ThreadLocalMap会扫描并清理这些“僵尸key”,但是如果不在使用这个ThreadLocal,那么清理逻辑就永远不会触发,就会导致内存泄漏问题。

  • 内存泄漏的发生需要同时满足两个条件:

    1. ThreadLocal实例不再被强引用
    2. 线程持续活动,导致ThreadLocalMap长期存在

2. 如何避免内存泄漏的发生

  • 如何避免内存泄漏:
    1. 使用完ThreadLocal后及时调用remove()方法。remove()方法会从ThreadLocalMap中显示地移除对应的entry,从而避免内存泄漏问题。
    2. 在线程池等线程复用的场景下,使用try-finally块可以确保即使发生异常,remove()方法也一定会被执行
      1
      2
      3
      4
      5
      6
      7
      8
      ThreadLocal<MyObject> threadLocal = new ThreadLocal<>();

      try {
      threadLocal.set(new MyObject());
      // 使用 threadLocal.get() 获取值进行操作
      } finally {
      threadLocal.remove(); // 避免内存泄漏
      }