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

As of JDK 18, no. JDK 18 omits enclosing instance fields from inner classes that don’t use it.

However, prior to JDK 18, yes, instances of anonymous inner classes hold on to a reference to their enclosing instances even if these references are never actually used. For example, 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.

Leave a Comment