Summary
This change causes javac to omit the synthetic field that holds a reference to the enclosing instance of an inner class, if the inner class doesn't capture any state from its enclosing instance.
Problem
When javac translates an inner class (a non-static member class, or an anonymous class in a non-static context), it generates a synthetic field to hold a reference to the enclosing instance. That is:
class T {
int x;
class I {
void f() {
System.err.println(x);
}
}
}
is translated as:
class T {
int x;
}
class T$I {
final T this$0;
T$I(T t) {
this$0 = t;
}
void f() {
System.err.println(this$0.x);
}
}
However the this$0
field is always emitted, even if the inner class doesn't capture any enclosing instance state and the field is unused.
The unused this$0
fields are a source of memory leaks if the state referenced by the enclosing instance is otherwise unused.
Additionally, the extra fields increase footprint and removing them may improve performance.
Solution
The solution is for javac to track whether a particular this$0
field is used when translating inner classes, and omit unused fields.
For example, consider the following program.
class T {
class I {
}
}
The inner class is currently translated as:
class T$I
minor version: 0
major version: 62
flags: (0x0020) ACC_SUPER
this_class: #2 // T$I
super_class: #8 // java/lang/Object
interfaces: 0, fields: 1, methods: 1, attributes: 3
...
{
final T this$0;
descriptor: LT;
flags: (0x1010) ACC_FINAL, ACC_SYNTHETIC
T$I(T);
descriptor: (LT;)V
flags: (0x0000)
Code:
stack=2, locals=2, args_size=2
0: aload_0
1: aload_1
2: putfield #1 // Field this$0:LT;
5: aload_0
6: invokespecial #7 // Method java/lang/Object."<init>":()V
9: return
LineNumberTable:
line 2: 0
}
SourceFile: "T.java"
NestHost: class T
InnerClasses:
#22= #2 of #19; // I=class T$I of class T
The proposed change is to omit the this$0
field, and instead translate the inner class as:
class T$I
minor version: 0
major version: 62
flags: (0x0020) ACC_SUPER
this_class: #7 // T$I
super_class: #2 // java/lang/Object
interfaces: 0, fields: 0, methods: 1, attributes: 3
...
{
T$I(T);
descriptor: (LT;)V
flags: (0x0000)
Code:
stack=1, locals=2, args_size=2
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 2: 0
}
SourceFile: "T.java"
NestHost: class T
InnerClasses:
#18= #7 of #15; // I=class T$I of class T
The change to javac's code generation will only be enabled by default for --release 18
and newer target levels, to provide behavioral compatibility for users depending on this implementation detail who upgrade to the latest JDK version and continue to target earlier language levels.
Specification
The proposed change to the implementation of javac can be seen in https://git.openjdk.java.net/jdk/pull/4966.
The use of the synthetic this$0
fields is an implementation detail of javac. The details of inner classes and anonymous classes that are mandated by the specification are unaffected by this change.
The form of an anonymous class's constructor is mandated by JLS 15.9.5.1. Similarly, for an inner class that is a member (rather than anonymous), the form of its constructor is mandated by JLS 8.8.1. This change does not affect the constructor parameters: the mandated formal parameter corresponding to the enclosing instance will still be included in the constructor signature; the constructor will simply ignore the parameter instead of saving it to a this$0
field if no state from the enclosing instance is captured.
JLS 13.1 requires every nested class to have "a symbolic reference to its immediately enclosing class or interface". This requirement is fulfilled by the InnerClasses
attribute and the NestHost
attribute, which both cause constant pool entries of kind CONSTANT_Class_info
that serve as symbolic references to the enclosing class T.
- csr of
-
JDK-8271623 Omit enclosing instance fields from inner classes that don't use it
-
- Resolved
-