Course contentsShow
← Node.js
Lesson 1883 of 1,98017. Performance Tuning and ProfilingFree lesson

Identifying Unbounded Cache Growth

You will detect cache structures that fail to implement eviction policies causing memory leaks.

In this lesson, you will learn how to identify and remediate memory exhaustion caused by caching structures that lack an automated eviction strategy.

The Pitfall of "Infinite" Caches

In Node.js, we often use plain JavaScript objects or Map instances to cache computed values or database responses to improve latency. While this is effective for performance, a common anti-pattern is the "unbounded cache." Because JavaScript objects do not automatically purge old entries, they will hold references to their contents indefinitely. If your application processes a high volume of unique requests, these references prevent the Garbage Collector (GC) from reclaiming memory, leading to an eventual heap overflow.

Identifying the Leak

If you have already analyzed your heap snapshots using previous techniques, you might notice that a specific Map or Object consistently grows in size across snapshots, even if the application's throughput remains constant. This is your smoking gun.

Consider this vulnerable implementation:

const userCache = new Map();

function getProcessedUser(id, data) {
  // Unbounded: new entries are added but never removed
  if (!userCache.has(id)) {
    userCache.set(id, expensiveTransform(data));
  }
  return userCache.get(id);
}

In the code above, if id is highly dynamic (e.g., a timestamp or unique session token), userCache will grow until the Node.js process reaches its max-old-space-size limit.

Remediation Strategies

To stop unbounded growth, you must implement an eviction policy. The most common approach is a Least Recently Used (LRU) policy. By using a library or a custom structure that caps the number of entries, you force the cache to drop the oldest elements when the limit is reached.

When you identify an unbounded cache in production, monitor the heapUsed metric alongside the cache size. If they grow in lockstep, your cache is effectively a memory leak. Replacing the raw Map with a size-limited cache (like lru-cache) will stabilize your heap usage by allowing the GC to reclaim references from evicted entries.

Key Takeaway: Any cache lacking a maximum size or TTL (Time-to-Live) policy is a high-risk memory leak waiting to manifest in production.