32

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?

Mike Valenty
9,0312 gold badges32 silver badges32 bronze badges
asked Feb 20, 2011 at 0:07

3 Answers 3

34

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 Outer1ドル.class. Disassembling the latter, the anonymous inner class, with javap -c yields:

Compiled from "Outer.java"
class Outer1ドル extends java.lang.Object implements java.lang.Runnable{
final Outer this0ドル;
Outer1ドル(Outer);
 Code:
 0: aload_0
 1: aload_1
 2: putfield #1; //Field this0ドル: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 this0ドル (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.

answered Feb 20, 2011 at 0:07
Sign up to request clarification or add additional context in comments.

9 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"?
@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.
@eckes correct, and a decade later, starting with JDK 18, we finally got rid of obsolete outer object references, JDK-8271717
@LaurenceGonsalves if you are at updating it, it’s worth noting that lambda expressions do not have this problem mentioned in the last section, so you may replace anonymous inner classes with lambda expressions were feasible, to avoid unnecessary outer object references since Java 8. (That’s again a point not specified in the JLS but just the way, compilers implement it)
|
7

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());
 }
}
answered Jan 30, 2014 at 9:56

1 Comment

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!
1

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).

answered Dec 18, 2013 at 0:52

1 Comment

with lambda, no inner class is created, thus no implicit this reference exists.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.