Anonymous classes and garbage collection

Anonymous classes and garbage collection

If an anonymous class instance is not going to reference the implicit this to its enclosing instance, which is actually the majority of the uses where we are just too lazy to define another class, the anonymous class instance needs to be created from a static context, such as a static factory method. Otherwise, there is highly likely a memory leak.

Say, we have a Configuration interface

  1. public interface Configuration {
  2.  
  3.   String getString(String name);
  4. }

and its builder ConfigurationBuilder

  1. public class ConfigurationBuilder {
  2.  
  3.   private final Map<String, String> map = new HashMap<String, String>();
  4.  
  5.   public ConfigurationBuilder set(String name, String value) {
  6.     map.put(name, value);
  7.     return this;
  8.   }
  9.  
  10.   public Configuration build() {
  11.     final Map<String, String> snapshot = new HashMap<String, String>(map);
  12.     return new Configuration() {
  13.  
  14.       @Override
  15.       public String getString(String name) {
  16.         return snapshot.get(name);
  17.       }
  18.     };
  19.   }
  20. }

ConfigurationBuilder builds implementations of Configuration by defensively copying the internal Map and creating an anonymous class instance wrapping the copy. While the logic is perfectly reasonable, a ConfigurationBuilder instance, which contains the prototypical internal Map, is going to live as long as a built anonymous class instance, because the anonymous class instance has an implicit reference to the enclosing instance when it was built, which was the ConfigurationBuilder instance. This is definitely unwanted, as we usually only need the built instance. To make the ConfigurationBuilder instance garbage collectible while the anonymous class instance it built is still being used, create anonymous class instances in a static context, e.g.,

  1. public class ConfigurationBuilder {
  2.  
  3.   private final Map<String, String> map = new HashMap<String, String>();
  4.  
  5.   public ConfigurationBuilder set(String name, String value) {
  6.     map.put(name, value);
  7.     return this;
  8.   }
  9.  
  10.   private static Configuration build(final Map<String, String> snapshot) {
  11.     return new Configuration() {
  12.  
  13.       @Override
  14.       public String getString(String name) {
  15.         return snapshot.get(name);
  16.       }
  17.     };
  18.   }
  19.  
  20.   public Configuration build() {
  21.     return build(new HashMap<String, String>(map));
  22.   }
  23. }

When an anonymous class instance is created in a static context, there is no enclosing instance or implicit reference, which can be confirmed by a debugger or the Java Language Specification 3.0 §15.9.2. Thus an instance of the new ConfigurationBuilder is garbage collectible and causes no memory leak. Of course, refactoring anonymous classes into private/package-private inner static classes is always a good idea.

Post new comment

The content of this field is kept private and will not be shown publicly.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.

More information about formatting options

To prevent automated spam submissions leave this field empty.