【问题标题】:If thread local map contains a weak reference to the threadlocal object, then why is it not garbage collected?如果线程本地映射包含对线程本地对象的弱引用,那么为什么不对其进行垃圾回收呢?
【发布时间】:2017-05-08 15:20:06
【问题描述】:

我有一个线程本地对象,它使用非静态内部类的对象进行初始化,如下所示:

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();
      }
    }
  }
}

我运行了这个程序,附加了 jconsole,多次触发了 GC,但内存使用量并没有下降。然后我进行了堆转储并分析了TObject 类创建的对象数量。它表明内存中所有 100,000 个对象都可用。

Screenshot of the heapdump, check out the object count

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

Screenshot of the heapdump with only 3000 objects

我不确定我是否理解:

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

【问题讨论】:

  • 只是想知道:与stackoverflow.com/questions/30992479/threadlocal-memory-leak 有什么关系吗?
  • 没有。那是关于线程局部变量是静态的。这是关于 threadlocal 是非静态内部类的对象的值。
  • 如果您还有其他如此重要的问题,请告诉我。仍在寻找“赏金”的“受害者”;-)
  • 哈哈!会做!虽然没有承诺。这样的问题很难提出:D

标签: java garbage-collection thread-local


【解决方案1】:

线程本地映射对实际值(在本例中为 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; 
        }
    }

当内部类是非静态的时,对象对StressTestThreadLocal有一个强引用,而StressTestThreadLocal又对tObjectThreadLocal有一个强引用(这是线程本地映射中的关键)。所以条目永远不会被垃圾收集。

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

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多