I'm reading Effective Java by Joshua Bloch. In Item 7: Eliminate Obsolete Object References of chapter 2 he states:
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. One 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 understand the concept of a WeakHashMap
. I implemented one for a mock cache:
/** Example courtesy of Baeldung */
public class WeakHashMapDemo {
public static void main(String[] args) {
/** Ideally, we want a Map implementation that allows GC to automatically delete unused objects.
* When a key of a big image object is not in use in our application in any place,
* that entry will be deleted from memory.
*/
WeakHashMap<UniqueImageName, BigImage> wMap = new WeakHashMap<>();
HashMap<UniqueImageName, BigImage> map = new HashMap<>();
BigImage bigImage = new BigImage("image_id");
UniqueImageName imageName = new UniqueImageName("name_of_big_image");
/** We are putting a BigImage object as a value and an imageName object reference as a key. */
wMap.put(imageName, bigImage);
System.out.println("Map does not contain imageName -> " + wMap.containsKey(imageName));
wMap.put(imageName, bigImage);
/** The key is set to null. */
imageName = null;
/** Forcing JVM to GC. */
System.gc();
System.out.println("Weak Map is empty -> " + wMap);
}
private static class UniqueImageName {
public UniqueImageName(String name_of_big_image) {
}
}
private static class BigImage {
public BigImage(String image_id) {
}
}
}
I also read this excellent article on weak references. However, I still don't understand using these concepts in the context of callbacks. How can I ensure memory leaks don't happen with a given API, using a WeakHashMap
, if clients don't explicitly deregister them?
I appreciate any help you can provide.
UPDATE:
It seems a few people need more clarity on the question. When I mean by "How can I ensure memory leaks don't happen with a given API, using a WeakHashMap
, if clients don't explicitly deregister them?" is how this can be implemented with code as I only seem to have a conceptual understanding at the moment. So I'd really appreciate it if someone could show me a code snippet on how this would be implemented on a mock API. Thanks again!
How can I ensure memory leaks don't happen with a given API, using a WeakHashMap, if clients don't explicitly deregister them?
A client of the API has to keep a strong reference to the listener, that's how it stays alive.
The API only holds a weak reference to the listener, to that it does not prevent the client from being garbage collected. When the client is garbage collected, the listener is also garbage collected.
Like this:
class ApiUser {
Listener listener;
void registerListener() {
listener = new Listener() {
@Override
public void event() {
// TODO
}
};
Api.registerListener(listener);
}
}
class Api {
static WeakHashMap<Listener, Object> listeners = new WeakHashMap<>();
static void registerListener(Listener listener) {
listeners.put(listener, null);
}
static void fireListeners() {
for (Listener listener : listeners.keySet()) {
if (listener != null) {
listener.event();
}
}
}
}
interface Listener {
void event();
}