Allow class garbage collection while an anonymous instance of the inner class is referenced elsewhere?

I have class A:

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

Instance b has an implicit reference to an instance of its outer class (which this can reference). Now another object gets a reference to this b using the getter method. This b cannot be collected from garbage due to the link.

Is there a way to get the ability to allow garbage collection of a nested instance of A , perhaps by dropping an explicit reference to an anonymous inner class?

+4
source share
2 answers

It is technically possible:

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

Checking javap anonymous class:

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

The implementation does not depend on the field name this$0 , since I suspect that this is a detail of the compiler implementation.

Potential areas of concern:

  • Security Manager may prohibit the reflection code.
  • I do not think that the Java platform defines exactly how the internal type relates to the external. That is, it is a detail of the compiler, and it would be legal if it were stupid to have an intermediate shell in the field - in the presence of other fields, eliminating the ambiguity of the link may be impossible.

In short, I would never do that .

If this is a concern, use a closed static inner class :

 public class A { private static class BImpl implements B { @Override public void method() { } } private final B b = new BImpl(); public B getB() { return b; } } 
+3
source

After calling the code B ref = (new A()).getB() Java heap will contain the variable ref , which points to the same anonymous object as (new A()).b , which, in turn, has an internal reference to your new A() . There are no other references to the new A() object.

Your question: how can we force the garbage collection of object A to save an object of anonymous b? Answer: you cannot, because if you could, what would happen to the code in b that uses the internal reference to A?

If you know that the code of your class B does not refer to its enclosing class, you can declare it static, that is, it will not receive an internal reference to its enclusing class. To do this, you need to make it a nested class, since you cannot eliminate anonymous classes as static:

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

If your code calls B ref = (new A()).getB() , in this situation the new A() object will be available for garbage collection, since there is no link to it.

+3
source

All Articles