In this lesson, you will learn how to use heap snapshots to detect "orphan" streams—objects that stay in memory forever because they were never properly closed or destroyed.
The Problem of Orphaned Streams
In a long-running Node.js server, streams are common. If you create a stream (like a Readable from a file or a Transform for data processing) but fail to call .destroy() or listen for the 'close' event, that object remains in the heap. Even after the data processing is finished, the stream object and its associated buffers stay in memory because the garbage collector cannot reclaim them while they are still referenced by event listeners or internal timers.
Identifying Leaks with Heap Snapshots
To find these, we use Heap Snapshots in the Chrome DevTools or the Node.js built-in profiler. Here is the workflow:
- Take a baseline: Start your server and capture a snapshot.
- Trigger the action: Perform an operation that creates streams (e.g., upload a file or process a request) multiple times.
- Take a second snapshot: Capture the state after the operations are complete and the garbage collector has run.
- Compare: Open the snapshots in DevTools and use the "Comparison" view. Look for "Constructor" entries that are increasing in count, specifically filtered for
Readable,Writable, or your custom stream classes.
If you see the number of stream instances increasing even after your requests have finished, you have a leak.
Example Scenario
Often, the culprit is an unclosed Transform stream or an event listener that keeps the stream alive:
// This creates a leak if 'data' finishes but the stream isn't destroyed
function processData(inputStream) {
const transform = new MyTransform();
inputStream.pipe(transform).on('data', () => {});
// If we don't handle 'error' or 'finish' to clean up,
// the 'transform' object might stay in the heap.
}
By inspecting the "Retainers" section in your heap snapshot, you can see exactly what is holding onto that stream—often an unremoved event listener or a closure capturing the stream variable.
Key Takeaway: Use heap snapshot comparisons to track stream object counts; if your stream count grows over time while the server is idle, you are likely failing to properly destroy or clean up finished stream resources.