如果线程本地映射包含对threadlocal对象的弱引用,那么为什么它不是垃圾回收?

Man*_*yal 4 java garbage-collection thread-local

我有一个threadlocal对象,它使用非静态内部类的对象初始化,如下所示:

public class StressTestThreadLocal {

  private final ThreadLocal<TObject> tObjectThreadLocal = ThreadLocal.withInitial(
      () -> new TObject(1000));

  private static ExecutorService executorService = Executors.newFixedThreadPool(4);

  private void startThread() {
    executorService.submit(tObjectThreadLocal::get);
  }

  public class TObject {
    List<Integer> test;

    TObject(int n) {
      test = new ArrayList<>();
      for (int i = 0; i < n; i++) {
        test.add(i);
      }
      System.out.println("Done making TObject " + UUID.randomUUID());
    }
  }

  public static void main(String[] args) {
    for (int i = 0; i < 100000; i++) {
      StressTestThreadLocal testThreadLocal = new StressTestThreadLocal();
      testThreadLocal.startThread();
    }
    while (true) {
      try {
        Thread.sleep(10000);
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

我运行了这个程序,附加了jconsole,多次触发GC,但内存使用量没有下降.然后我拿了一个heapdump并分析了创建TObject类的对象数.它显示内存中有所有100,000个对象.

heapdump的屏幕截图,查看对象计数

我使内部类静态意味着它不再强烈引用外部类对象并再次运行相同的代码.这里触发GC显着降低了内存使用量,内存中的对象数量仅为3000左右.

只有3000个对象的heapdump的屏幕截图

我不确定我理解的是:

在第一种情况下,outerObject和innerObject保持对彼此的强引用,但是没有一个在其他任何地方被强引用.如果threadlocalmap只包含对threadlocal变量(TObject)的弱引用,并且我们没有StressTestThreadLocal在其他任何地方保存对外部对象的引用,为什么threadlocal对象不符合垃圾回收的条件?为什么让内部类静态自动解决这个问题呢?

Roh*_*ram 5

线程局部映射具有对实际值的强引用(在这种情况下为TObject).只有地图的关键字(ThreadLocal)才是弱引用.将密钥作为弱引用的原因是在不再引用密钥时从地图中删除条目. http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/lang/ThreadLocal.java?av=f#298

static class ThreadLocalMap {
    static class Entry extends WeakReference<ThreadLocal<?>> {
        Object value;
        Entry(ThreadLocal<?> k, Object v) {
            super(k);
            value = v; 
        }
    }
Run Code Online (Sandbox Code Playgroud)

当内部类是非静态的时,该对象具有对StressTestThreadLocal的强引用,而StressTestThreadLocal又具有对tObjectThreadLocal(这是线程本地映射中的键)的强引用.因此,条目永远不会被垃圾收集.

TLM - > TLM.Entry.Value(TObject) - > StressTestThreadLocal - > TLM.Entry.Key(ThreadLocal)
其中TLM = Thread本地映射