Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Як профілювати використання пам'яті?». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)Профілювання пам'яті - це порівняння знімків heap до і після підозрілих дій (Heap snapshot у Chrome DevTools, heapdump у Node.js) для пошуку об'єктів, що не звільняються. **Ключове:** якщо кількість чи розмір об'єктів зростають після повторів дії і не повертаються після ручного GC, це ознака витоку пам'яті.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення## У браузері (Chrome DevTools) ### 1) Базовий сценарій "чи росте heap?" 1. Відкрий DevTools -> **Memory**. 2. Вибери **Heap snapshot** -> **Take snapshot** (знімок №1). 3. Виконай підозрілі дії (навігація по SPA, відкриття/закриття модалок, списків тощо). 4. **Take snapshot** знову (знімок №2). 5. Порівняй: вкладка **Comparison** -> відсортуй за **Δ Size**/**Δ Count**. Шукай "зростаючих" власників (classes/конструктори), **Detached DOM nodes** і великі масиви/Map. Що дивитися: - **Retainers** (ланцюжки утримання) - покажуть, яке посилання заважає GC. - **Distance** - мале значення означає близько до "root"; такі об'єкти рідко звільняються. ### 2) Реал-тайм "де створюються об'єкти" **Allocation instrumentation on timeline**: 1. Memory -> **Allocation instrumentation on timeline** -> **Start**. 2. Відтвори сценарій 10-30 сек. 3. **Stop** -> клікай по піках графіка -> дивись **Allocation stacks** (рядки коду, де відбуваються аллокації). Плюси: показує гарячі точки виділень "за місцем". Мінуси: шумно, але чудово для ловлі сплесків. ### 3) Легкий режим **Sampling allocation profiler** - менш точний, але майже без оверхеду. Зручний, коли складно довго відтворювати баг. ### 4) Таймлайн пам'яті У вкладці **Performance** увімкни метрику **Memory** -> зроби запис. Якщо після "ручного GC" (іконка смітника) heap **не повертається** приблизно до вихідного рівня, є витік. ### 5) Швидкі перевірки з консолі ```javascript // Поточний розмір хипа (лише Chromium): performance.memory.usedJSHeapSize // (Експериментально) детальніше за агентами: if (performance.measureUserAgentSpecificMemory) { performance.measureUserAgentSpecificMemory().then(console.log); } ``` ## У Node.js ### 1) Інспекція через DevTools Запусти з інспектором і відкрий у Chrome: ```javascript node --inspect index.js # або при старті дебагу node --inspect-brk index.js ``` У Chrome -> chrome://inspect -> **Open dedicated DevTools** -> вкладка **Memory** -> знімки heap, як у браузері. ### 2) Знімок хипа в рантаймі ```javascript npm i heapdump ``` ```javascript import heapdump from 'heapdump'; setInterval(() => { heapdump.writeSnapshot(`./heap-${Date.now()}.heapsnapshot`); }, 60_000); ``` Відкрий .heapsnapshot у DevTools і порівняй. ### 3) Швидкі лічильники ```javascript setInterval(() => { const m = process.memoryUsage(); console.log({ rss: Math.round(m.rss/1024/1024)+'MB', heapUsed: Math.round(m.heapUsed/1024/1024)+'MB', heapTotal: Math.round(m.heapTotal/1024/1024)+'MB', ext: Math.round(m.external/1024/1024)+'MB' }); }, 5000); ``` Якщо `heapUsed` пиляється вгору і не повертається, є підозра на витік. ### 4) GC-трейси (діагностика) ```javascript node --trace-gc index.js ``` Дивись частоту і тривалість пауз; постійне зростання "after GC" - поганий знак. ## Для SPA/React (і взагалі компонентів) ### 1) Негласний стандартний протокол перевірки 1. Відкрий сторінку А -> зроби **Heap snapshot #1**. 2. Перейди на сторінку B, повернися на А (або відкрий/закрий компонент) -> **#2**. 3. Натисни **GC** (іконка смітника) -> **#3**. 4. Порівняй #1 і #3. Якщо об'єкти сторінки А/компонента **залишилися**, а їх **кількість/розмір ростуть** після повторів, це витік. Шукай: - **Detached DOM nodes**; - обробники `EventListener` без `removeEventListener`; - активні `Timeout/Interval`; - "зомбі"-підписки (WebSocket, RxJS); - кеші (`Map/Set`) без TTL/очищення; - великі структури в Context/Redux, які ніхто не скидає. ### 2) Специфіка React - Перевіряй `useEffect` на **cleanup** (повернення функції): ```javascript useEffect(() => { const id = setInterval(fetchData, 5000); return () => clearInterval(id); // обов'язково! }, []); ``` - React DevTools **Profiler**: компонент має **Unmount**-итися при відході зі сторінки. - Стеж за `ref` на DOM: після unmount вони мають стати `null`. ## Методика пошуку витоку (коротко) 1. **Зафіксуй симптом**: "через N хвилин heap росте з X до Y". 2. **Звузь сценарій**: мінімально відтворювана послідовність дій. 3. **Збери дані**: Heap snapshot до/після, Allocation timeline, Perf Memory. 4. **Знайди власника**: клас/конструктор, де відбувається зростання -> дивись **Retainers**. 5. **Зв'яжи з кодом**: рядок/файл через Allocation stacks або sourcemaps. 6. **Виправ**: додай cleanup/видали посилання/обмеж кеш. 7. **Перевір**: повтори кроки 1-3 - зростання має зникнути. 8. **Автоматизуй** (за можливості): e2e-скрипт, який вимірює `usedJSHeapSize` по кроках. ## Часті "підказки" у звітах - **Detached HTMLDivElement** росте -> забули зняти слухача/посилання на DOM. - Ростуть масиви/об'єкти в **Map/Set** -> немає TTL/LRU-очищення. - Багато **Timeout/Interval** -> немає `clearTimeout/clearInterval`. - Купа екземплярів компонента **після навігації** -> не розмонтовується або "тримають" замикання/підписки. - Витік у **third-party** бібліотеці -> обгорни у свій адаптер з cleanup або заміни.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.