我有一个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
实例进行垃圾收集?
答案 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
,因为我怀疑这是一个编译器实现细节。
潜在的问题领域:
简而言之,我永远不会这样做。
如果这是一个问题,使用私有静态内部类:
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()
对象将可用于垃圾回收,因为不存在对它的引用。