Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 440
Laurence Gonsalves Avatar asked Feb 20 '11 00:02

Laurence Gonsalves


People also ask

What is true for anonymous class?

1. Which is true about an anonymous inner class? A. It can extend exactly one class and implement exactly one interface.

How do anonymous classes work?

Anonymous classes enable you to make your code more concise. They enable you to declare and instantiate a class at the same time. They are like local classes except that they do not have a name. Use them if you need to use a local class only once.

Can we create instance of anonymous class?

Anonymous classes are inner classes with no name. Since they have no name, we can't use them in order to create instances of anonymous classes. As a result, we have to declare and instantiate anonymous classes in a single expression at the point of use. We may either extend an existing class or implement an interface.

Is an anonymous class is instantiated once defined?

An anonymous class is a class not given a name and is both declared and instantiated in a single statement. You should consider using an anonymous class whenever you need to create a class that will be instantiated only once.


2 Answers

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.

like image 179
Laurence Gonsalves Avatar answered Sep 29 '22 19:09

Laurence Gonsalves


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());     } } 
like image 45
Delphin Avatar answered Sep 29 '22 18:09

Delphin