Problem Statement
Explain memory leaks in JavaScript. What causes them and how do you prevent them? Provide examples.
Explanation
Memory leaks occur when objects that are no longer needed remain in memory because they are still referenced, preventing garbage collection.
How JavaScript garbage collection works:
Mark and sweep algorithm identifies reachable objects. Objects without references are collected. Circular references are handled automatically in modern engines. Understanding this helps prevent leaks.
Common causes of memory leaks:
Forgotten event listeners that hold references to elements and callbacks. Timers not cleared with clearTimeout or clearInterval. Closures capturing large objects unnecessarily. Detached DOM nodes still referenced in JavaScript. Global variables never garbage collected. Circular references in old browsers.
Event listener leaks:
Adding listeners without removing creates persistent references. Each listener holds reference to callback and element. Multiple calls add multiple listeners. Always remove listeners on cleanup. Use weak references when possible.
Timer leaks:
SetTimeout and setInterval keep callbacks in memory. Intervals run forever unless cleared. Callbacks may reference large objects. Always clear timers when done. Store timer IDs for cleanup.
Closure leaks:
Closures capture entire scope chain. May hold references to large objects unnecessarily. Extract only needed data before creating closure. Be mindful of what closures capture.
DOM node leaks:
Removing node from DOM does not remove JavaScript references. Storing nodes in arrays or objects prevents collection. Event listeners keep nodes alive. Clear all references when removing nodes.
Detecting memory leaks:
Use Chrome DevTools Memory Profiler. Take heap snapshots before and after actions. Compare snapshots to find growing objects. Look for detached DOM nodes. Monitor memory usage over time. Use performance.memory API.
Preventing memory leaks:
Always remove event listeners on cleanup. Clear all timers. Use WeakMap and WeakSet for object keys. Avoid global variables. Be careful with closures. Remove DOM references. Use proper lifecycle management in frameworks.
Best practices:
Implement cleanup methods in classes. Use weak references when appropriate. Clear caches periodically. Test for leaks during development. Monitor production memory usage. Use tools to detect leaks early.
Framework considerations:
React: cleanup in useEffect return or componentWillUnmount. Vue: cleanup in beforeDestroy or onUnmounted. Angular: cleanup in ngOnDestroy. All frameworks need manual cleanup of external resources.
Understanding and preventing memory leaks is crucial for building stable, long-running JavaScript applications.