Memory Profiling
Профилирование памяти через heap snapshots (Chrome DevTools / v8.writeHeapSnapshot) — обязательный навык Senior: ключевые понятия retained size vs shallow size и умение сравнивать два snapshot для нахождения утечки.
// Node.js: heap snapshot программноconst v8 = require('v8');const path = require('path');
function takeSnapshot(label = '') { const filename = path.join( process.cwd(), `heap-${label}-${Date.now()}.heapsnapshot` ); v8.writeHeapSnapshot(filename); console.log('Snapshot:', filename); return filename;}
// Открыть в Chrome DevTools: Memory → Load Profile// Или: node --heap-snapshot-signal=SIGUSR2 app.js// kill -USR2 <pid> ← снять снимок в рантайме без рестарта// Retained size vs Shallow size// Shallow size: память, занятая САМИМ объектом (поля, не референсы)// Retained size: память, которая освободится при GC объекта// = объект + всё, доступное ТОЛЬКО через него
// root → A → B (10 MB буфер)// Shallow(A) = маленький (несколько полей)// Retained(A) = большой (включает B и его 10 MB)
// Workflow для диагностики утечки:// 1. Снять snapshot #1 (baseline)// 2. Воспроизвести action N раз (login/logout, open/close)// 3. Принудительный GC: если есть global.gc() → вызвать// 4. Снять snapshot #2// 5. Comparison view: Objects in #2 but not in #1 → кандидаты на утечку
// Типичные источники утечек:// - Глобальные Map/Set без TTL или LRU eviction// - Event listeners без removeEventListener// - Closures, удерживающие DOM nodes или большие буферы// - Timers (setInterval) без clearInterval// process.memoryUsage() для мониторинга трендаfunction monitorMemory(label) { const m = process.memoryUsage(); console.log(`[${label}]`, { heapUsed: (m.heapUsed / 1024 / 1024).toFixed(2) + ' MB', heapTotal: (m.heapTotal / 1024 / 1024).toFixed(2) + ' MB', rss: (m.rss / 1024 / 1024).toFixed(2) + ' MB', external: (m.external / 1024 / 1024).toFixed(2) + ' MB', // external: C++ объекты (Buffer, TypedArray) — вне V8 heap });}
// Демо: утечка через event listenerconst EventEmitter = require('events');const ee = new EventEmitter();
function leak() { const bigData = Buffer.alloc(1024 * 1024); // 1 MB ee.on('data', () => void bigData); // bigData удерживается listener-ом}
for (let i = 0; i < 20; i++) leak(); // 20 MB утечкиmonitorMemory('after leak');// heapUsed + external вырастут на ~20 MBИтог: Профилирование памяти требует понимания retained vs shallow size и умения сравнивать два heap snapshot; большинство утечек — незамеченные ссылки через event listeners, closures и глобальные кэши без eviction.