How Lodash Memoize Handles Garbage Collection

The _.memoize function in the Lodash JavaScript library does not automatically garbage collect cached results, which can lead to memory retention issues if left unmanaged. By default, Lodash retains cached values using strong references in a standard map-like structure, meaning entries remain in memory for the lifetime of the memoized function. To enable garbage collection or prevent memory leaks, developers must either manually clear the cache or replace Lodash's default cache constructor with alternative implementations such as WeakMap or a Least Recently Used (LRU) cache.

The Default Cache Mechanism

By default, Lodash’s _.memoize instantiates an internal cache using JavaScript's native Map (or a fallback object structure in older environments), assigned via _.memoize.Cache.

const _ = require('lodash');

const compute = (obj) => { /* heavy computation */ };
const memoizedCompute = _.memoize(compute);

When an argument is evaluated, Lodash stores the resolved cache key and its corresponding result inside this cache instance:

  1. Strong References: Standard Map objects maintain strong references to both keys and values.
  2. Preventing GC: Because the references remain active inside the memoized function's .cache property, the JavaScript garbage collector cannot sweep either the arguments or the returned values, even if they are no longer needed elsewhere in the application.
  3. Unbounded Growth: Each unique key generates a new entry. In long-running Node.js processes or single-page applications, calling a memoized function with continuously varying inputs creates an unbounded cache, causing memory consumption to grow steadily over time.

Enabling Garbage Collection with WeakMap

If your cached computations rely exclusively on object keys, you can integrate garbage collection directly by overriding memoize.Cache with a WeakMap.

_.memoize.Cache = WeakMap;

const processUserData = _.memoize((user) => {
  return { id: user.id, computedProfile: expensiveTransform(user) };
});

Using a WeakMap fundamentally changes how garbage collection behaves:

Manual Memory Management

For scenarios where WeakMap is impractical—such as when caching results based on primitive values—you must manage memory manually using the .cache property exposed on the memoized function.

Using an Eviction Strategy (LRU Cache)

To maintain bounded memory usage without manual clearing, you can replace the default cache constructor with a Least Recently Used (LRU) cache. This limits the maximum number of items held in memory.

const LRU = require('lru-cache');

// Configure a custom cache factory
_.memoize.Cache = function() {
  return new LRU({ max: 500 });
};

const cachedFunction = _.memoize(expensiveCalculation);

When the item limit is reached, the LRU cache automatically evicts the oldest unused items. Once evicted from the cache, those items lose their strong references and are automatically cleaned up by the JavaScript engine during the next garbage collection cycle.