Do anonymous classes *always* maintain a reference to their enclosing instance?

I'm working with some code where one object, "foo", is creating another object, "bar", and passing it a Callable. After this foo will return bar, and then I want foo to become unreachable (ie: available for garbage collection).

My initial thought was to just create the Callable anonymously. eg:

class Foo {
  ...

  public Bar createBar() {
    final int arg1 = ...
    final int arg2 = ...
    final int arg3 = ...
    return new Callable<Baz>() {
      @Override
      public Baz call() {
        return new Baz(arg1, arg2, arg3);
      }
    };
  }
}

It occurred to me that this might not actually work as desired, however, as an inner class typically keeps a reference to its enclosing object. I don't want a reference to the enclosing class here, because I want the enclosing object to be collected while the Callable is still reachable.

On the other hand, detecting that the enclosing instance is never actually referred to should be pretty trivial, so perhaps the Java compiler is smart enough to not include a reference in that case.

So... will an instance of an anonymous inner class hold on to a reference to its enclosing instance even if it never actually uses the enclosing instance reference?

Yes, instances of anonymous inner classes hold on to a reference to their enclosing instances even if these references are never actually used. This code:

public class Outer {
  public Runnable getRunnable() {
    return new Runnable() {
      public void run() {
        System.out.println("hello");
      }
    };
  }
}

When compiled with javac generates two class files, Outer.class and Outer$1.class. Disassembling the latter, the anonymous inner class, with javap -c yields:

Compiled from "Outer.java"
class Outer$1 extends java.lang.Object implements java.lang.Runnable{
final Outer this$0;

Outer$1(Outer);
  Code:
   0:   aload_0
   1:   aload_1
   2:   putfield        #1; //Field this$0:LOuter;
   5:   aload_0
   6:   invokespecial   #2; //Method java/lang/Object."<init>":()V
   9:   return

public void run();
  Code:
   0:   getstatic       #3; //Field java/lang/System.out:Ljava/io/PrintStream;
   3:   ldc     #4; //String hello
   5:   invokevirtual   #5; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   8:   return

}

The putfield line shows that a reference to the enclosing instance is being stored in the field this$0 (of type Outer) by the constructor even though this field is never used again.

This is unfortunate if you're attempting to create small potentially long-lived objects with anonymous inner classes as they'll hold onto the (potentially large) enclosing instance. A workaround is to use an instance of a static class (or a top-level class) instead. This is unfortunately more verbose.

Comp310: Anonymous Inner Class Techniques, Always remember that an anonymous inner class is an object, not a class. inner class to a variable -- you only need to do that if you intend to reference it more to hold the reference to the enclosing instance ("thisEnclosingClass" above). Referencing Enclosing Anonymous Inner Classes. The Java syntax enables one to reference the class instance one is currently in, "this", and the outermost enclosing class instantance, "Classname.this" (where "Classname" is the name of outermost named class.). Unfortunately, there is no Java syntax construct for referencing an enclosing anonymous inner class.

You can easily turn a nested anonymous-class into a "static" anonymous-class by introducing a static method in your class.

import java.util.ArrayList;


public class TestGC {
    public char[] mem = new char[5000000];
    public String str = "toto";

    public interface Node {
        public void print();
    }

    public Node createNestedNode() {
        final String str = this.str;
        return new Node() {
            public void print() {
                System.out.println(str);
            }
        };
    }

    public static Node createStaticNode(TestGC test) {
        final String str = test.str;
        return new Node() {
            public void print() {
                System.out.println(str);
            }
        };
    }

    public Node createStaticNode() {
        return createStaticNode(this);
    }

    public static void main(String... args) throws InterruptedException {
        ArrayList<Node> nodes = new ArrayList<Node>();
        for (int i=0; i<10; i++) {
            // Try once with createNestedNode(), then createStaticNode()
            nodes.add(new TestGC().createStaticNode());
            System.gc();
            //Thread.sleep(200);
            System.out.printf("Total mem: %d  Free mem: %d\n", Runtime.getRuntime().totalMemory(), Runtime.getRuntime().freeMemory());
        }
        for (Node node : nodes)
            node.print();
        nodes = null;
        System.gc();
        //Thread.sleep(200);
        System.out.printf("Total mem: %d  Free mem: %d\n", Runtime.getRuntime().totalMemory(), Runtime.getRuntime().freeMemory());
    }
}

Inner Classes - Learning Java [Book], Their even sweeter cousins, anonymous inner classes , are another powerful What does it mean for a class definition to be “inside” another class definition? The another class), Brain always requires an enclosing instance of Animal to “ hold” it. For more information, consult a full language reference, such as Java� An anonymous class cannot access local variables in its enclosing scope that are not declared as final or effectively final. Like a nested class, a declaration of a type (such as a variable) in an anonymous class shadows any other declarations in the enclosing scope that have the same name. See Shadowing for more information.

The static alternative (in this case) is not much larger (1 line):

public class Outer {
  static class InnerRunnable implements Runnable {
      public void run() {
        System.out.println("hello");
      }
    }
  public Runnable getRunnable() {
    return new InnerRunnable();
  }
}

BTW: if you use a Lambda in Java8 there will be no nested class generated. However I am not sure if the CallSite objects which get passed around in that case hold an reference to the outer instance (if not needed).

Inner Classes - Learning Java, 4th Edition [Book], In Java, anonymous inner classes play part of the role of closures in other another class), Brain always requires an enclosing instance of Animal to “hold” it. To do this requires that the inner class Brain be accessible and that we use a special This statement enables us to refer to the class simply as MigrationPattern . An inner class instance can't exist independent to the enclosing class instance. An inner class instance always exist within the instance of the enclosing class. Since, it's always associated with the enclosing class instance and it has direct access to all the members of the enclosing class - even the private members.

[PDF] Inner Classes Specification, platform, and development environment, refer to the JavaSoft web site How does the Java Language Specification change for inner classes? . . . . . . . . 19 including both class and instance members of enclosing classes, and local variables must always be null, to match the constructor of the actual superclass , Object. Since they have no name, we can't extend them. For the same reason, anonymous classes cannot have explicitly declared constructors. In fact, the absence of a constructor doesn't represent any problem for us for the following reasons: we create anonymous class instances at the same moment as we declare them.

Inner class, In object-oriented programming (OOP), an inner class or nested class is a class declared This notion does not represent the Wheel s as Wheel s in a more general Each instance of these classes has a reference to an enclosing instance (i.e. an Anonymous inner classes are also used where the event handling code is� Like local classes, anonymous classes can capture variables; they have the same access to local variables of the enclosing scope: An anonymous class has access to the members of its enclosing class. An anonymous class cannot access local variables in its enclosing scope that are not declared as final or effectively final.

For non-static anonymous inner classes that will always keep reference to their enclosing instance..i.e the inner class always have ‘this’ reference (this$0) to the outer class. This will lead to memory leak- since this is preventing GC from freeing up the occupied memory.

Comments
  • Definitely use a static class. It's not that verbose IMO.
  • @deepc You need to add fields for each parameter, which is 1 line per parameter, and a constructor, which adds 2 lines plus 1 for each parameter. So for a simple 3-argument Runnable that's at least 8 more lines of code. The anonymous inner class syntax is already pretty verbose compared to a proper lambda expression syntax: a single method class has 4 lines of boilerplate you wouldn't need with lambda expressions. So a 3-parameter 1-line lambda expression in Java becomes 13 lines of code. How big would it have to be for you to consider it "that verbose"?
  • you are right with your line "statistics". Maybe it is personal taste. But after all the call() method has to be in there as well. And now we have enough code to justify a separate class (not necessarily a top level class as you point out). To me the code looks cleaner this way. Even more so if that method is longer than a few lines.
  • The question is if the compiler has to actually create that field, if it knows it is not used (and if the JIT has to retain the unseen putfield).
  • @eckes That is indeed a good question. I was quite disappointed when I saw that the field was still created even when not used, as this seems like a pretty trivial optimization to make.
  • This isn't quite an answer to the question, but is a good solution to how to write a "static anonymous-class" in a much less verbose way. Thanks!
  • with lambda, no inner class is created, thus no implicit this reference exists.