Card 07/ 09
All 9 cards
GotchaDifficulty: Advanced1 min
A Memory Leak in a Language That Collects Memory
A service that has run for nine days is using eleven gigabytes. Restarting it fixes the problem for another nine days. Nothing in the code allocates anything unusual.
class SessionRegistry {
private static final Map<String, Session> active = new HashMap<>();
static void login(String id, Session s) { active.put(id, s); }
}There is no logout. Every session ever created is still in that map, the map is a static field, and a static field is a garbage collection root. Every one of those sessions is reachable, so every one of them is alive.
This is the same rule as the four-line example, in a setting that looks nothing like it: the collector has not changed its mind about anything, and the only question is still whether a path from a root reaches the object. Here it does, nine days of them.
| Holder | What it accumulates |
|---|---|
A static collection with no removal | Everything ever added |
| A listener registered and never removed | The listener and everything it points at |
| A cache with no size limit or expiry | Every key ever requested |
A ThreadLocal in a pooled thread | One object per thread, for the life of the pool |
Give every long-lived collection a removal path before it has anything in it — a logout, an eviction policy, a bounded cache. A map that only ever grows is a leak that has not had time yet.
- Java
- Garbage Collection
- Performance