When analyzing a retainer trace from memlab, look for these common patterns in the codebase:
Event listeners attached to global objects (like window or document) or long-living objects prevent garbage collection of the objects referenced in their callbacks.
Fix: Always call removeEventListener when a component unmounts or the listener is no longer needed.
A DOM node is removed from the document tree but is still referenced by a JavaScript variable. While detachedness is a good signal for a memory leak, it's not always a bug. For example, websites sometimes intentionally cache detached navigation trees.
Fix: Signal the detached nodes to the user first. Ask the user first before nulling the references or changing the code, as the detached nodes might be part of an intentional cache. If confirmed as a leak, ensure variables holding DOM references are set to null when the node is removed, or limit their scope.
Variables declared without var, let, or const (in non-strict mode) or explicitly attached to window remain in memory forever.
Fix: Use strict mode, properly declare variables, and avoid global state.
Closures can unintentionally keep references to large objects in their outer scope.
Fix: Nullify large objects when they are no longer needed, or refactor the closure to not capture unnecessary variables.
Data structures used for caching (like objects, Arrays, or Maps) that grow without limits.
Fix: Implement caching limits, use LRU caches, or use WeakMap/WeakSet for data associated with object lifecycles.