Skip to content
Plurafy

Memory Profiling and Optimization

Memory management is crucial for Node.js performance. Learn to profile, analyze, and optimize memory usage in your applications.

Analyze and optimize memory usage to prevent memory leaks, reduce consumption, and improve application performance and stability.

Heap: Dynamic memory allocation Stack: Function calls and local variables Memory Leak: Unreferenced objects not garbage collected Fragmentation: Inefficient memory layout Garbage Collection: Automatic memory reclamation

  • Node Inspector: Built-in debugging
  • clinic.js: Performance profiling
  • 0x: Flame graph generation
  • Chrome DevTools: Remote debugging
  • heapdump: Heap snapshots
function getMemoryUsage() {
const mem = process.memoryUsage();
return {
rss: Math.round(mem.rss / 1024 / 1024) + 'MB',
heapTotal: Math.round(mem.heapTotal / 1024 / 1024) + 'MB',
heapUsed: Math.round(mem.heapUsed / 1024 / 1024) + 'MB',
external: Math.round(mem.external / 1024 / 1024) + 'MB',
arrayBuffers: Math.round(mem.arrayBuffers / 1024 / 1024) + 'MB',
};
}
// Log memory usage periodically
setInterval(() => {
console.log('Memory usage:', getMemoryUsage());
}, 30000);
// Express endpoint
app.get('/metrics/memory', (req, res) => {
res.json(getMemoryUsage());
});
class LeakDetector {
constructor(threshold = 100 * 1024 * 1024) { // 100MB
this.threshold = threshold;
this.samples = [];
this.maxSamples = 10;
}
sample() {
const heapUsed = process.memoryUsage().heapUsed;
this.samples.push({
timestamp: Date.now(),
heapUsed,
});
// Keep only recent samples
if (this.samples.length > this.maxSamples) {
this.samples.shift();
}
this.analyzeGrowth();
}
analyzeGrowth() {
if (this.samples.length < 3) return;
const firstSample = this.samples[0];
const lastSample = this.samples[this.samples.length - 1];
const growth = lastSample.heapUsed - firstSample.heapUsed;
const timeDiff = lastSample.timestamp - firstSample.timestamp;
// Check for consistent growth
if (growth > this.threshold) {
console.warn('Potential memory leak detected!', {
growth: Math.round(growth / 1024 / 1024) + 'MB',
timespan: Math.round(timeDiff / 1000) + 's',
growthRate: (growth / timeDiff * 1000).toFixed(2) + 'MB/s',
});
// Trigger heap dump
this.dumpHeap();
}
}
dumpHeap() {
const heapdump = require('heapdump');
heapdump.writeSnapshot(`./heap-${Date.now()}.heapsnapshot`);
}
start(interval = 60000) {
setInterval(() => this.sample(), interval);
}
}
const leakDetector = new LeakDetector();
leakDetector.start(60000); // Check every minute
class ObjectPool {
constructor(factory, resetFn, size = 10) {
this.factory = factory;
this.resetFn = resetFn;
this.available = [];
this.in_use = new Set();
// Pre-allocate objects
for (let i = 0; i < size; i++) {
this.available.push(factory());
}
}
acquire() {
let obj;
if (this.available.length > 0) {
obj = this.available.pop();
} else {
obj = this.factory();
}
this.in_use.add(obj);
return obj;
}
release(obj) {
if (!this.in_use.has(obj)) {
throw new Error('Object not from this pool');
}
this.resetFn(obj);
this.in_use.delete(obj);
this.available.push(obj);
}
size() {
return {
available: this.available.length,
in_use: this.in_use.size,
};
}
}
// Usage
const bufferPool = new ObjectPool(
() => Buffer.allocUnsafe(1024 * 1024), // 1MB buffers
(buf) => buf.fill(0), // Reset buffer
50 // Pool size
);
// Using pool
const buffer = bufferPool.acquire();
try {
// Use buffer
buffer.write('data');
} finally {
bufferPool.release(buffer);
}
const fs = require('fs');
// BAD: Loading entire file into memory
async function processFileBad(filePath) {
const data = fs.readFileSync(filePath, 'utf-8');
const lines = data.split('\n');
return lines.map(line => line.toUpperCase());
}
// GOOD: Streaming processing
function processFileGood(filePath) {
return new Promise((resolve, reject) => {
const lines = [];
const stream = fs.createReadStream(filePath, { encoding: 'utf-8' });
stream.on('data', (chunk) => {
const chunkLines = chunk.split('\n');
chunkLines.forEach(line => {
if (line) lines.push(line.toUpperCase());
});
});
stream.on('end', () => resolve(lines));
stream.on('error', reject);
});
}
// BETTER: Process lines without storing all
function processFileStreaming(filePath) {
const stream = fs.createReadStream(filePath, { encoding: 'utf-8' });
stream.on('data', (chunk) => {
// Process chunk immediately
const lines = chunk.split('\n');
lines.forEach(line => {
if (line) {
// Do something with line without storing
console.log(line.toUpperCase());
}
});
});
return new Promise((resolve, reject) => {
stream.on('end', resolve);
stream.on('error', reject);
});
}
// Regular Map - keeps references alive
const cache = new Map();
// WeakMap - allows garbage collection
const weakCache = new WeakMap();
class CacheExample {
constructor() {
this.strongCache = new Map();
this.weakCache = new WeakMap();
}
setStrong(key, value) {
this.strongCache.set(key, value);
}
setWeak(key, value) {
// key must be an object, won't prevent garbage collection
this.weakCache.set(key, value);
}
getWeak(key) {
return this.weakCache.get(key);
}
}
// Usage
const cache = new CacheExample();
let obj = { id: 1 };
cache.setWeak(obj, 'some data');
console.log(cache.getWeak(obj)); // 'some data'
obj = null; // Object can now be garbage collected
// WeakMap entry will be automatically removed
Terminal window
# Install clinic.js
npm install -g clinic
# Doctor profiling
clinic doctor -- node server.js
# Flame graph
clinic flame -- node server.js
# Bubbleprof
clinic bubbleprof -- node server.js
const v8 = require('v8');
const fs = require('fs');
// Trigger heap snapshot
function createHeapSnapshot() {
const timestamp = Date.now();
const snapshotStream = v8.writeHeapSnapshot(`./heap-${timestamp}.heapsnapshot`);
return new Promise((resolve, reject) => {
snapshotStream.on('finish', () => {
console.log(`Heap snapshot written to heap-${timestamp}.heapsnapshot`);
resolve();
});
snapshotStream.on('error', reject);
});
}
// Endpoint to trigger snapshot
app.post('/admin/heap-snapshot', async (req, res) => {
try {
await createHeapSnapshot();
res.json({ success: true, message: 'Heap snapshot created' });
} catch (error) {
res.status(500).json({ error: error.message });
}
});
  1. Monitor Memory

    • Use memory profiling tools
    • Set up alerts for high usage
    • Track memory trends
    • Test under load
  2. Prevent Leaks

    • Remove event listeners
    • Clear timers and intervals
    • Properly close connections
    • Avoid circular references
  3. Optimize Usage

    • Use streams for large data
    • Implement object pooling
    • Leverage caching appropriately
    • Use WeakMap for caches
  4. Garbage Collection

    • Understand GC pauses
    • Tune GC parameters if needed
    • Monitor GC frequency
    • Avoid forcing GC
  5. Testing

    • Load test applications
    • Check memory under stress
    • Monitor long-running processes
    • Use heap snapshots for analysis
  • Server Applications: Prevent memory exhaustion
  • Microservices: Resource-constrained environments
  • Data Processing: Handle large datasets efficiently
  • Real-time Systems: Consistent performance
  • Long-running Processes: Detect memory leaks early