nodejs-profiling

v2026.09.24

Node.js performance profiling with V8 CPU profiler, heap analysis, and perf_hooks. Use for identifying bottlenecks and memory leaks. USE WHEN: user mentions "Node.js performance", "profiling", "memory leak", asks about "V8 profiler", "heap snapshot", "CPU profile", "perf_hooks", "event loop lag", "Node.js optimization" DO NOT USE FOR: Java/Python profiling - use respective skills instead

GitHub
Install command
npx skhub add claude-dev-suite/nodejs-profiling
Markdown
SKILL.md

Node.js Performance Profiling

When NOT to Use This Skill

  • Java/JVM profiling - Use the java-profiling skill for JFR, jcmd, and GC tuning
  • Python profiling - Use the python-profiling skill for cProfile and memory_profiler
  • Frontend performance - Use browser DevTools for client-side profiling
  • Database query optimization - Use database-specific profiling tools
  • Network performance - Use tools like curl, ab, or specialized load testers

Deep Knowledge: Use mcp__documentation__fetch_docs with technology: nodejs for comprehensive profiling guides, V8 flags, and optimization techniques.

V8 CPU Profiling

Command Line Profiling

# CPU profile (generates .cpuprofile)
node --cpu-prof --cpu-prof-dir=./profiles app.js

# V8 profile (generates .log)
node --prof app.js
node --prof-process isolate-*.log > processed.txt

# Heap snapshot on signal
node --heapsnapshot-signal=SIGUSR2 app.js
kill -USR2 <pid>

Programmatic Profiling

import { Session } from 'inspector';
import { writeFileSync } from 'fs';

const session = new Session();
session.connect();

// Start CPU profiling
session.post('Profiler.enable');
session.post('Profiler.start');

// Your code here...

// Stop and get profile
session.post('Profiler.stop', (err, { profile }) => {
  writeFileSync('profile.cpuprofile', JSON.stringify(profile));
});

Memory Analysis

Heap Statistics

import v8 from 'v8';

const heapStats = v8.getHeapStatistics();
console.log({
  heapUsed: heapStats.used_heap_size,
  heapTotal: heapStats.total_heap_size,
  heapLimit: heapStats.heap_size_limit,
  external: heapStats.external_memory,
});

// Detailed heap space info
const heapSpaces = v8.getHeapSpaceStatistics();
heapSpaces.forEach(space => {
  console.log(`${space.space_name}: ${space.space_used_size}`);
});

Memory Tracking

import { performance, PerformanceObserver } from 'perf_hooks';

// Track memory at intervals
const memoryTracker = setInterval(() => {
  const usage = process.memoryUsage();
  console.log({
    rss: usage.rss,           // Resident Set Size
    heapTotal: usage.heapTotal,
    heapUsed: usage.heapUsed,
    external: usage.external,
    arrayBuffers: usage.arrayBuffers,
  });
}, 1000);

High-Resolution Timing

perf_hooks API

import { performance, PerformanceObserver } from 'perf_hooks';

// Mark start/end
performance.mark('operation-start');
await someOperation();
performance.mark('operation-end');

// Measure duration
performance.measure('operation', 'operation-start', 'operation-end');

// Observer for async measurements
const obs = new PerformanceObserver((list) => {
  const entries = list.getEntries();
  entries.forEach(entry => {
    console.log(`${entry.name}: ${entry.duration}ms`);
  });
});
obs.observe({ entryTypes: ['measure', 'function'] });

// Cleanup
performance.clearMarks();
performance.clearMeasures();

Async Context Tracking

import { AsyncLocalStorage, AsyncResource } from 'async_hooks';

const storage = new AsyncLocalStorage<{ requestId: string }>();

// Track request timing across async operations
function trackRequest(requestId: string) {
  storage.run({ requestId }, async () => {
    const start = performance.now();
    await handleRequest();
    const duration = performance.now() - start;
    console.log(`Request ${requestId}: ${duration}ms`);
  });
}

Common Bottleneck Patterns

CPU-Bound Issues

// ❌ Bad: Blocking the event loop
function processLargeArray(arr: number[]): number {
  return arr.reduce((sum, n) => sum + expensiveComputation(n), 0);
}

// ✅ Good: Use worker threads
import { Worker, isMainThread, parentPort, workerData } from 'worker_threads';

if (isMainThread) {
  const worker = new Worker(__filename, { workerData: largeArray });
  worker.on('message', (result) => console.log(result));
} else {
  const result = workerData.reduce((sum, n) => sum + expensiveComputation(n), 0);
  parentPort?.postMessage(result);
}

I/O-Bound Issues

// ❌ Bad: Sequential I/O
for (const file of files) {
  await fs.readFile(file);  // One at a time
}

// ✅ Good: Parallel I/O with concurrency limit
import pLimit from 'p-limit';
const limit = pLimit(10);

await Promise.all(
  files.map(file => limit(() => fs.readFile(file)))
);

Memory Leaks

// ❌ Bad: Unbounded cache
const cache = new Map();
function getUser(id: string) {
  if (!cache.has(id)) {
    cache.set(id, fetchUser(id));  // Never cleaned up
  }
  return cache.get(id);
}

// ✅ Good: LRU cache with max size
import { LRUCache } from 'lru-cache';
const cache = new LRUCache<string, User>({
  max: 1000,
  ttl: 1000 * 60 * 5,  // 5 minutes
});

// ❌ Bad: Event listener leak
element.addEventListener('click', handler);  // Never removed

// ✅ Good: Cleanup listeners
const abortController = new AbortController();
element.addEventListener('click', handler, { signal: abortController.signal });
// Later: abortController.abort();

GC Pressure

// ❌ Bad: Creating many temporary objects
function process(items: Item[]) {
  return items.map(item => ({
    ...item,
    computed: compute(item),
  }));
}

// ✅ Good: Mutate in place when safe
function process(items: Item[]) {
  for (const item of items) {
    item.computed = compute(item);
  }
  return items;
}

// ✅ Good: Object pooling
class ObjectPool<T> {
  private pool: T[] = [];

  acquire(): T {
    return this.pool.pop() || this.create();
  }

  release(obj: T) {
    this.reset(obj);
    this.pool.push(obj);
  }
}

Optimization Techniques

Buffer Optimization

// ❌ Bad: Many small allocations
const chunks: Buffer[] = [];
for (const data of stream) {
  chunks.push(Buffer.from(data));
}
const result = Buffer.concat(chunks);

// ✅ Good: Pre-allocate when size known
const buffer = Buffer.allocUnsafe(totalSize);  // Faster, uninitialized
let offset = 0;
for (const data of stream) {
  offset += data.copy(buffer, offset);
}

Stream Processing

// ❌ Bad: Loading entire file in memory
const data = await fs.readFile('large-file.json');
const parsed = JSON.parse(data);

// ✅ Good: Stream processing
import { createReadStream } from 'fs';
import { parser } from 'stream-json';
import { streamArray } from 'stream-json/streamers/StreamArray';

const pipeline = createReadStream('large-file.json')
  .pipe(parser())
  .pipe(streamArray());

for await (const { value } of pipeline) {
  await processItem(value);
}

V8 Optimization Hints

// Force V8 to optimize a function
function criticalFunction(x: number): number {
  // Called many times with same types
  return x * 2;
}
// Warm up
for (let i = 0; i < 10000; i++) criticalFunction(i);

// Avoid deoptimization patterns:
// - Don't change object shapes after creation
// - Don't use delete on object properties
// - Don't use arguments object, use rest parameters
// - Don't use with statement
// - Keep function polymorphism low

Profiling Checklist

CheckToolCommand
CPU hotspotsCPU profilenode --cpu-prof app.js
Memory usageHeap statsv8.getHeapStatistics()
Memory leaksHeap snapshot--heapsnapshot-signal
Event loop lagperf_hooksmonitorEventLoopDelay()
Async operationsAsync hooksasync_hooks module
Function timingperf_hooksperformance.measure()

GC Tuning

# Increase heap size
node --max-old-space-size=4096 app.js

# GC logging
node --trace-gc app.js

# Expose GC for manual control
node --expose-gc app.js
# In code: global.gc();

Anti-Patterns

Anti-PatternWhy It's WrongCorrect Approach
Using setImmediate() for CPU workBlocks event loopUse worker threads for CPU-intensive tasks
Synchronous file operationsBlocks entire processUse async fs.promises API
Large synchronous JSON parsingFreezes event loopStream large JSON or use worker threads
Callback hellHard to profile, error-proneUse async/await for cleaner async code
Not using connection poolingCreates too many connectionsUse connection pools (pg, mysql2)
console.log() in productionSlow, blocks event loopUse structured logging (pino, winston)
Loading entire file into memoryMemory exhaustionUse streams for large files
Manual cache without TTL/limitsMemory leaksUse LRU cache with size/time limits
Not monitoring event loop lagUndetected performance degradationUse perf_hooks.monitorEventLoopDelay()
delete on object propertiesDeoptimizes objectsSet to undefined or use Map

Quick Troubleshooting

IssueDiagnosisSolution
High CPU usageTight loops, inefficient algorithmsProfile with --cpu-prof, optimize hot paths
Memory growing continuouslyMemory leak (unbounded cache, listeners)Take heap snapshots, compare over time
Event loop lagLong synchronous operationsUse worker threads or break into async chunks
GC pauses causing latency spikesHeap too large or fragmentedReduce heap size, optimize object creation
Slow startup timeToo many synchronous requiresLazy load modules, use dynamic imports
FATAL ERROR: CALL_AND_RETRY_LASTOut of memoryIncrease --max-old-space-size or fix memory leak
High memory usageLarge buffers, string operationsUse streams, avoid string concatenation
Unhandled promise rejectionsAsync errors not caughtAdd .catch() or use try/catch with async/await
Function not optimized by V8Contains deopt triggersCheck with --trace-deopt, avoid problematic patterns
Slow JSON operationsLarge payloadsStream JSON or use faster parsers (simdjson)
Discovery
Tags

No tags published for this skill.

Version
Latest version metadata

Version

v2026.09.24

Published

Sep 24, 2026

Category

Uncategorized

License

MIT

Source path

skills/profiling/nodejs

Default branch

main

Latest commit

9496306

Tree SHA

fe4e2f1