首页 > 解决方案 > 弱引用和线程

问题描述

我试图了解如何WeakReference工作以及为什么下面的代码片段会永远运行,即为什么dummy字符串永远不会被 GC,尽管似乎删除了对它的所有引用?

import java.lang.ref.WeakReference;
import java.util.concurrent.TimeUnit;

public class Test {
  public static void main(String[] args) {
    String dummy = new String("sdfsdfsdf");
    WeakReference<String> dummyRef = new WeakReference<>(dummy);

    Thread test = new Thread(
      () -> {
        while (true) {
          try {
            TimeUnit.SECONDS.sleep(1);
          } catch (InterruptedException e) {
          }

          String s = dummyRef.get();
          if (s == null) {
            System.out.println("String GC'd. Quitting.");
            break;
          }

          System.out.println(s);
          s = null;
        }
      }
    );
    test.start();

    try {
      dummy = null;
      TimeUnit.SECONDS.sleep(30);
    } catch (InterruptedException e) {
    }
  }
}

GC 是真的在这里运行,还是 lambda 持有对周围堆栈帧中对象的引用,尽管从未实际dummy直接使用该变量?我必须进行哪些更改才能使代码按预期工作?

这个用例是一个类的工厂方法,它将产生一个线程,该线程定期调用返回对象的方法。如果创建的对象被销毁(即允许对象被 GC),我希望线程退出。

标签: javamultithreadinggarbage-collection

解决方案


如果包含激活垃圾收集器的原因,您的测试应该可以工作。让我们添加一个只吃内存的线程:

public class Test {
  public static void main(String[] args) {
    String dummy = new String("sdfsdfsdf");
    WeakReference<String> dummyRef = new WeakReference<>(dummy);

    // This thread creates arrays and chucks them.
    // Marked as a daemon to ensure it doesn't stop the program
    // from quitting.
    Thread memEater = new Thread(
      () -> {
          for( int i=1;;i++) {
              int[] arr = new int[6553500];
              arr[0] = i;
              try {
                  TimeUnit.SECONDS.sleep(1);
              } catch (InterruptedException e) {
              }
          }
      }
    );
    memEater.setDaemon(true);
    memEater.start();

    Thread test = new Thread(
      () -> {
        while (true) {
          try {
            TimeUnit.SECONDS.sleep(1);
          } catch (InterruptedException e) {
          }

          String s = dummyRef.get();
          if (s == null) {
            System.out.println("String GC'd. Quitting.");
            break;
          }

          System.out.println(s);
          s = null;
        }
      }
    );
    test.start();

    try {
      dummy = null;
      TimeUnit.SECONDS.sleep(30);
    } catch (InterruptedException e) {
    }
  }
}

我从中得到的输出是:

% java Test                                                                                                 
sdfsdfsdf
sdfsdfsdf
sdfsdfsdf
sdfsdfsdf
String GC'd. Quitting.

推荐阅读