1

我读到 JVM gc 在找到一个无法访问的对象后运行该对象的 finalize() 方法。然后它检查对象是否仍然无法访问,如果不是,则将其删除。所以我写了一个finalize,使它的对象的引用再次可用:

public class Stuff {

 List<Object> list;

 public Stuff(List<Object> list) {
  this.list = list;
 }

 @Override
 protected void finalize() throws Throwable {
  list.add(this);
  System.out.println("A Stuff is finalized");
 }
}

下面是主要方法:

public class Main { 
 public static void main(String[] args) {
  List<Object> list = new ArrayList<>();
  list.add(new Stuff(list));
  list.remove(0);
  System.gc();
  System.out.println(list.get(0));
 }
}

运行 gc,因为“A stuff is finalized”出现在标准输出上,但 main 中的 printline 之后会引发 IndexOutOfBoundsException。我可以完成这项工作吗?

我通常根本不使用 finalize,我只是觉得看看 finalize 是否可以使其对象的引用再次可用会很有趣。我可以做这个工作吗?

4

2 回答 2

2

终结器在专用终结器线程中运行,您编写的是线程不安全的代码。例如,使用同步集合。

另一个陷阱是,仅仅调用System.gc()并不能保证终结器在方法调用返回时已经运行。终结器只是在终结器线程的队列中排队——即使是这样。要解决这个问题,您应该真正使用同步助手,例如CountDownLatch并调用System.gc()两到三次,以获得良好的衡量标准。

在这里,您的代码使用上述想法进行了改进:

public class Stuff {

  static final List<Stuff> list = Collections.synchronizedList(new ArrayList<Stuff>());
  static final CountDownLatch cdl = new CountDownLatch(1);

  @Override protected void finalize() {
    list.add(this);
    cdl.countDown();
  }

  public static void main(String[] args) throws Exception {
    list.add(new Stuff());
    list.remove(0);
    System.gc();
    System.gc();
    cdl.await();
    System.out.println(list.size());
  }
}
于 2013-11-06T19:45:48.420 回答
0
List<Object> list = new ArrayList<>();
  list.add(new Stuff(list));
  list.remove(0);
  System.gc();
  System.out.println(list.get(0));

您正在创建Object未实施的类Lis实例列表。所以是意料之中的。StufffinalizeArrayIndexOutOfBound

于 2013-11-06T19:46:36.123 回答