Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Збирач сміття (GC) у JavaScript». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Збирач сміття це механізм JS-рушія (наприклад V8), який автоматично звільняє пам'ять, зайняту об'єктами, що стали недосяжними.** Рішення ухвалюється не за «потрібністю», а за досяжністю (reachability): рушій стартує від коренів (`globalThis`, стек викликів, замикання) і позначає все, до чого можна дійти по ланцюжку посилань; усе непозначене видаляється. Класичний алгоритм це mark-and-sweep, у V8 він розділений на Minor GC (Scavenge, для молодих об'єктів) і Major GC (Mark-Sweep-Compact, для старих). Саме тому циклічні посилання не є проблемою: якщо до циклу не дістатися з кореня, обидва об'єкти буде видалено. ```javascript let user = { name: 'Alice' }; let admin = user; // two references to one object user = null; // still reachable through admin admin = null; // now unreachable, GC will free it ``` **Ключове:** GC видаляє не «непотрібне», а недосяжне; поки на об'єкт є живе посилання з кореня, він лишається в пам'яті.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**Збирач сміття (Garbage Collector) це механізм JavaScript-рушія (наприклад V8), який автоматично звільняє пам'ять, зайняту об'єктами, що стали недосяжними.** Тобто GC прибирає з пам'яті все, на що більше немає посилань, щоб не марнувати ресурси і не допустити переповнення пам'яті. ## Теорія ### TL;DR - GC звільняє пам'ять автоматично, ручного `free()` у JavaScript немає. - Критерій один: досяжність (reachability), а не «логічна потрібність» об'єкта. - Корені досяжності: глобальний об'єкт, стек викликів, живі замикання. - Базовий алгоритм: mark-and-sweep, іноді з фазою compact (ущільнення пам'яті). - V8 має два режими: Minor GC (Scavenge) для молодих об'єктів і Major GC (Mark-Sweep-Compact) для старих. - Циклічні посилання не заважають GC: важливо лише, чи можна дійти до об'єкта від кореня. - Витік це не поломка GC, а живе посилання на те, що вам уже не потрібне. ### Швидкий приклад Рушій керує пам'яттю так: 1. Виділяє пам'ять під новий об'єкт: ```javascript let user = { name: 'Tim' }; // memory allocated ``` 2. Змінна перестає посилатися на об'єкт: ```javascript user = null; // the old reference is gone ``` 3. GC розуміє, що об'єкт `{ name: 'Tim' }` недосяжний, і в найближчому циклі звільнить пам'ять. ### Ключове поняття: досяжність (reachability) > У JS пам'ять очищається не вручну, а за принципом досяжності. Досяжні (reachable) об'єкти: - глобальні змінні (`window`, `global`, `globalThis`); - локальні змінні в стеку, поки виконується функція; - об'єкти, на які посилаються інші досяжні об'єкти. Недосяжні (unreachable): - об'єкти, на які немає посилань із досяжних місць. ```javascript let user = { name: 'Alice' }; let admin = user; // two references to one object user = null; // still reachable through admin admin = null; // now unreachable, GC will remove it ``` ### Приклад із ланцюжком і циклом посилань ```javascript function createUser() { const user = { name: 'Bob' }; const address = { city: 'Paris' }; user.addr = address; address.owner = user; // circular reference return user; } let person = createUser(); ``` Тут `user` посилається на `address`, а `address` назад на `user`. Якщо потім: ```javascript person = null; ``` обидва об'єкти стають недосяжними, бо до них більше не дійти від кореня, і GC звільнить обидва попри циклічне посилання. JS-збирач уміє розпізнавати такі цикли: він рахує не кількість посилань, а досяжність від коренів. ### Алгоритм роботи на прикладі V8 V8 (рушій Chrome і Node.js) використовує алгоритм **mark-and-sweep** (позначити і прибрати): 1. **Mark (позначення).** GC проходить від кореневих об'єктів (root: `window`, `global`, стек викликів) і позначає все, що досяжне. 2. **Sweep (очищення).** Усе, що не було позначене, видаляється з пам'яті. 3. **Compact (ущільнення).** Іноді пам'ять ущільнюється, щоб прибрати фрагментацію і звільнити суцільні блоки. Крім того, у V8 є два типи циклів: - **Minor GC (Scavenge)** для «молодих» об'єктів: нових, які зазвичай живуть недовго. Працює часто і швидко. - **Major GC (Mark-Sweep-Compact)** для «старих» об'єктів: тих, що пережили кілька Minor GC. Працює рідше і довше. ### Як GC впливає на продуктивність - GC працює автоматично і переважно асинхронно, але іноді спричиняє невеликі паузи (GC pause). - Чим більше «живих» об'єктів, тим довше триває цикл збирання. - Витоки пам'яті заважають GC: він вважає «висячі» посилання живими і не може нічого звільнити. > Оптимальний код це менше довгоживучих об'єктів плюс своєчасне розривання посилань. ### Чого GC не робить | Не робить | Чому | | --- | --- | | Не очищає все одразу | Щоб не блокувати роботу програми | | Не бачить «логічної непотрібності» | Він дивиться лише на відсутність посилань, це не інтелект | | Не керується вручну | Немає `free()` як у C++; оператор `delete` у JS видаляє властивість, а не пам'ять | | Не чистить глобальні об'єкти | Вони завжди досяжні через `window` або `globalThis` | ### Як дивитися на GC і пам'ять **У браузері:** - Chrome DevTools, вкладка **Memory**, «Heap snapshot»; - вкладка **Performance**, секція **Memory**, кнопка «Record»; - консоль: `performance.memory.usedJSHeapSize`. **У Node.js:** - прапорець `--inspect`, пакети `heapdump`, `clinic.js`, метод `process.memoryUsage()`. Підсумкова таблиця: | Що | Опис | | --- | --- | | **GC (Garbage Collector)** | Механізм автоматичного звільнення пам'яті | | **Принцип** | Видаляє об'єкти, до яких не можна «дійти» від кореня | | **Головний алгоритм** | Mark-and-sweep | | **Проблема витоків** | Об'єкт усе ще досяжний, але вже не потрібен | | **Рішення** | Розривати посилання, чистити таймери і слухачів | | **Плюси** | Простота, безпека роботи з пам'яттю | | **Мінуси** | Потенційні паузи і приховані витоки | ### Типові помилки | Помилка | Що відбувається | | --- | --- | | Глобальні змінні | Лишаються досяжними назавжди, GC їх ніколи не прибере | | Таймери і слухачі без очищення | Посилання утримують замикання разом з усім їхнім вмістом | | DOM-посилання після видалення елемента | Вузол видалено з документа, але `ref` тримає його, тож GC безсилий | | Замикання з великими об'єктами | Витоки через область видимості: одна маленька функція тримає мегабайти | Окремо варто пам'ятати ще дві речі. По-перше, «об'єкт більше не потрібен» і «об'єкт недосяжний» це різні твердження: GC розуміє тільки друге. По-друге, немає способу примусово і надійно викликати GC з коду застосунку, тому будь-яка оптимізація зводиться до того, щоб вчасно відпускати посилання, а не «просити» збирача попрацювати.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.