Diagnose a Memory Leak in a JavaScript Web Application
AndurilFrontend Engineer · OnsiteLast updated
Tests your ability to identify common JavaScript memory leak patterns—such as detached DOM trees, uncleared event subscriptions, and unbounded closures—and verify resolution using browser heap profiling techniques.
Scenario
You are investigating a performance degradation report in a long-running operational dashboard. Operators leave the application open for 12+ hours during drone and sensor monitoring missions. Over time, the browser tab consumes over 2 GB of memory, becomes sluggish, and eventually crashes with an Out of Memory error.
You have isolated the issue to a dynamic telemetry widget that mounts and unmounts whenever the user switches between sensor feeds. Below is a simplified version of the component implementation:
class TelemetryStreamer {
constructor(sensorId, containerElement) {
this.sensorId = sensorId;
this.container = containerElement;
this.cache = [];
// Create UI element
this.displayEl = document.createElement('div');
this.displayEl.className = 'telemetry-readout';
this.container.appendChild(this.displayEl);
// Global event listener for incoming websocket telemetry packets
window.addEventListener('sensor-update', (e) => {
if (e.detail.id === this.sensorId) {
this.render(e.detail.data);
}
});
// Periodic stats polling
this.timer = setInterval(() => {
this.cache.push({ timestamp: Date.now(), status: 'active' });
}, 1000);
}
render(data) {
this.displayEl.textContent = `Sensor ${this.sensorId}: ${JSON.stringify(data)}`;
}
destroy() {
// Invoked when switching sensor feeds
this.container.removeChild(this.displayEl);
}
}
Tasks
- Identify the Leaks: Analyze the
TelemetryStreamerclass and identify all distinct mechanisms preventing instances and associated DOM elements from being garbage collected afterdestroy()is called. - Provide a Leak-Free Implementation: Refactor
TelemetryStreamerto ensure all references, timers, and listeners are properly decoupled and cleaned up upon teardown. - Diagnostic Strategy: Describe the exact steps you would take in Chrome DevTools (or equivalent browser tooling) to:
- Confirm the leak using heap snapshots.
- Identify detached DOM trees and retaining paths.
- Validate that memory usage reaches a stable steady state after your fix.
Hints
Hint 1
Check what roots keep the instance alive: look at global objects (`window`), long-running timers, and anonymous closures.
Hint 2
Removing a child element from the DOM via `removeChild` does not free it if an active JavaScript reference or closure still retains that element.
Hint 3
To isolate leaks in DevTools, compare two heap snapshots taken before and after repeated mount/unmount cycles, filtering by 'Objects allocated between snapshots'.
Follow-up questions
- How does using `AbortController` simplify event listener cleanup compared to manually storing and removing bound handler references?
- When would you choose `WeakMap` or `WeakRef` over an explicit `.destroy()` lifecycle method for caching DOM elements or telemetry state?
- How can you automate memory leak detection in CI/CD pipelines before code reaches production?
Solution walkthrough is for members
Get the full walkthrough with reference code and complexity analysis, plus Kitty Coach at 300 messages a day. Hints, the code runner and community answers stay free.