Effective Java says:
A third common source of memory leaks
is listeners and other callbacks. If
you implement an API where clients
register callbacks but don’t
deregister them explicitly, they will
accumulate unless you take some
action. The best way to ensure that
callbacks are garbage collected
promptly is to store only weak
references to them, for instance, by
storing them only as keys in a
WeakHashMap.
I am a beginner in Java. Could somebody teach me how to create weak references in callbacks and tell me how they solve the memory leak problems? Thanks.
Read this article
The key take aways are :
You can think of direct references as
strong references that require no
extra coding to create or access the
object. The remaining three types of
references are subclasses of the
Reference class found in the
java.lang.ref package. Soft references
are provided by the SoftReference
class, weak references by the
WeakReference class, and phantom
references by PhantomReference.
Soft references act like a data cache.
When system memory is low, the garbage
collector can arbitrarily free an
object whose only reference is a soft
reference. In other words, if there
are no strong references to an object,
that object is a candidate for
release. The garbage collector is
required to release any soft
references before throwing an
OutOfMemoryException.
Weak references are weaker than soft
references. If the only references to
an object are weak references, the
garbage collector can reclaim the
memory used by an object at any time.
There is no requirement for a low
memory situation. Typically, memory
used by the object is reclaimed in the
next pass of the garbage collector.
Phantom references relate to cleanup
tasks. They offer a notification
immediately before the garbage
collector performs the finalization
process and frees an object. Consider
it a way to do cleanup tasks within an
object.
followed by the WeakListModel listing which I won't post to avoid cluttering this response.
To illustrate the concept with a quick (crude) example, consider the following:
public interface ChangeHandler {
public void handleChange();
}
public class FileMonitor {
private File file;
private Set<ChangeHandler> handlers = new HashSet<ChangeHandler>();
public FileMonitor(File file) {
this.file = file;
}
public void registerChangeHandler(ChangeHandler handler) {
this.handlers.add(handler);
}
public void unregisterChangeHandler(ChangeHandler handler) {
this.handlers.remove(handler);
}
...
}
If a client class then uses this FileMonitor
API, they might do this:
public class MyClass {
File myFile = new File(...);
FileMonitor monitor = new FileMonitor(myFile);
public void something() {
...
ChangeHandler myHandler = getChangeHandler();
monitor.registerChangeHandler(myHandler);
...
}
}
If the author of the MyClass
then forgets to call unregisterChangeHandler()
when it's done with the handler, the FileMonitor
's HashSet
will forever reference the instance that was registered, causing it to remain in memory until the FileMonitor
is destroyed or the application quits.
To prevent this, Bloch is suggesting using a weak-referencing collection instead of the HashSet
, so that if your instance of MyClass
is destroyed, the reference will be removed from the monitor's collection.
You might replace the HashSet
in FileMonitor
with a WeakHashMap
and use the handlers as the keys, since the latter will automatically remove the handler from the collection when all other references to the object are gone.
Here you may find a clear and practical explanation as well:
Memory leaks in Android — identify, treat and avoid