在其他地方引用匿名内部类实例时允许类的垃圾收集?

时间:2010-01-13 11:26:39

标签: java garbage-collection inner-classes

我有一个A类:

public class A {
    private B b = new B() { public void method() { do something } };

    public B getB() { return b; }
}

public interface B { void method(); }

实例b具有其外部类的实例的隐式引用(可以由this引用)。现在,另一个对象通过getter方法获取对此b的引用。由于引用,此b无法进行垃圾回收。

有没有办法可以通过重置匿名内部类中的显式引用来允许对封闭的A实例进行垃圾收集?

2 个答案:

答案 0 :(得分:3)

技术上可行:

public class HasInner {
  public static interface Foo {}

  private static <T> T release(T instance, Object ref) {
    try {
      Class<?> type = instance.getClass();
      for (Field field : type.getFields()) {
        if (!field.isAccessible()) {
          field.setAccessible(true);
        }
        if (field.get(instance) == ref) {
          field.set(instance, null);
        }
      }
    } catch (IllegalAccessException e) {
      throw new IllegalStateException(e);
    }
    return instance;
  }

  public Foo makeFoo() {
    return release(new Foo() {}, this);
  }

  public static void main(String[] args) {
    new HasInner().makeFoo();
  }
}

javap 检查匿名类:

Compiled from "HasInner.java"
final class HasInner$1 extends java.lang.Object implements HasInner$
Foo{
    final HasInner this$0;
    HasInner$1(HasInner);
}

该实现不依赖于字段名this$0,因为我怀疑这是一个编译器实现细节。

潜在的问题领域:

  • 安全管理员可以禁止反射代码。
  • 我不认为 Java平台确切地定义了内部类型如何引用外部。也就是说,它是一个编译器实现细节,如果是愚蠢的,在字段中有一个中间包装器是合法的 - 在其他字段存在的情况下,消除引用可能是不可能的。

简而言之,我永远不会这样做

如果这是一个问题,使用私有静态内部类

public class A {
  private static class BImpl implements B {
    @Override public void method() {
    }
  }

  private final B b = new BImpl();

  public B getB() { return b; }
}

答案 1 :(得分:3)

在您的代码调用B ref = (new A()).getB()之后,Java堆将包含一个变量ref,该变量指向与(new A()).b相同的匿名对象,后者又对其封闭的{{1}具有内部引用。 }}。没有其他对new A()对象的引用。

您的问题是,我们如何在保持匿名b对象存活的同时强制对A对象进行垃圾收集?答案是你不能,因为如果可以的话,b中使用内部引用A的代码会发生什么?

如果您知道B类的代码未引用其封闭类,则可以将其声明为静态,这意味着它不会接收对其enclusing类的内部引用。为此,您需要将其设置为嵌套类,因为您无法将匿名类生成为静态类:

new A()

如果您的代码在这种情况下调用public class A { static class Bimpl implements B { public void method() { do something } }; private B b = new Bimpl(); public B getB() { return b; } } public interface B { void method(); } B ref = (new A()).getB()对象将可用于垃圾回收,因为不存在对它的引用。