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
-
public interface Configuration {
-
-
String getString(String name);
-
}
and its builder ConfigurationBuilder
-
public class ConfigurationBuilder {
-
-
private final Map<String, String> map = new HashMap<String, String>();
-
-
public ConfigurationBuilder set(String name, String value) {
-
map.put(name, value);
-
return this;
-
}
-
-
public Configuration build() {
-
final Map<String, String> snapshot = new HashMap<String, String>(map);
-
return new Configuration() {
-
-
@Override
-
public String getString(String name) {
-
return snapshot.get(name);
-
}
-
};
-
}
-
}
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.,
-
public class ConfigurationBuilder {
-
-
private final Map<String, String> map = new HashMap<String, String>();
-
-
public ConfigurationBuilder set(String name, String value) {
-
map.put(name, value);
-
return this;
-
}
-
-
private static Configuration build(final Map<String, String> snapshot) {
-
return new Configuration() {
-
-
@Override
-
public String getString(String name) {
-
return snapshot.get(name);
-
}
-
};
-
}
-
-
public Configuration build() {
-
return build(new HashMap<String, String>(map));
-
}
-
}
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