GotchaDifficulty: Intermediate2 min

Unbounded Caches and the Slow Memory Leak

It runs for weeks, then memory fills and the process is killed.

A cache with no size limit and no eviction keeps every key it is ever given, so it grows with every distinct value read and never shrinks. It behaves like a slow memory leak: the process works for weeks, then runs out of memory and is killed, with no change in traffic to explain it.

The symptom. Every application server's memory climbs steadily over three weeks. Traffic is flat. Then the servers are killed by the operating system for using too much memory, restart, and the climb begins again.

Memory on one application server, an illustrative pattern
  1. Day 1

    Deploy and restart

    Memory at 400 MB; the in-process cache is empty.

  2. Day 7

    Steady climb

    Memory at 1.4 GB; traffic unchanged.

  3. Day 14

    Still climbing

    Memory at 2.6 GB; responses unaffected.

  4. Day 21

    Killed

    Memory reaches the 4 GB limit; the process is killed and restarts empty.

The trigger. A developer added a cache inside the application to speed up product lookups: a map from product id to product, filled on every miss. It had no maximum size and no expiry. In testing it held a few hundred products.

The mechanism. Each distinct key adds an entry, and nothing ever removes one. Traffic is flat, but the set of different products, users or search terms read over three weeks keeps growing, so memory grows with it, and the process looks healthy right up to the day it is killed.

The cost. Every server fails on a cycle, often at the same time if they were deployed together, and each restart throws away the whole cache, sending a wave of misses to the database.

The remedy. Give every cache a maximum size, in items or bytes, and an eviction rule such as LRU, and give entries a TTL so stale ones leave on their own. Watch the cache's size as a metric, so growth shows up long before the limit does.

So never create a cache without deciding its size: a cache with no limit is a memory leak that has not failed yet.