# Сборка мусора

> Автоматическое освобождение памяти, которая больше недоступна программе; основные подходы — подсчёт ссылок и трассирующие сборщики (mark-sweep, копирующие, поколенческие).

Источник: https://llmdocs.ru/wiki/garbage-collection/ · обновлено 2026-11-09 · лицензия CC BY-SA 4.0

- **Появление**: Lisp, Джон Маккарти (1959)
- **Подходы**: подсчёт ссылок, трассировка (mark-sweep, mark-compact, копирование)
- **Улучшения**: поколения, инкрементальная и конкурентная сборка
- **Языки с GC**: Java, C#, Go, Python, JavaScript, Ruby, Haskell
- **Без GC**: C, C++ (RAII), Rust (владение)
- **Метрики**: паузы, пропускная способность, расход памяти
**Сборка мусора** (англ. *garbage collection*, GC) — автоматическое управление памятью, при котором среда выполнения сама находит объекты, недоступные программе, и освобождает занятую ими память. Программисту не нужно вызывать `free` или `delete`, а значит исчезают целые классы ошибок: утечки из-за забытого освобождения, повторное освобождение и обращение к уже освобождённой памяти (use-after-free). Платой становятся дополнительный расход процессора и памяти и паузы в работе программы. Сравнение с ручным управлением и моделью владения Rust — в статье [Сборка мусора vs ручное управление памятью](https://llmdocs.ru/wiki/gc-vs-manual-memory.md).

## История

Сборку мусора изобрёл Джон Маккарти в 1959 году для языка Lisp: список, ставший ненужным, должен был возвращаться в свободную память автоматически.[2] Долгое время GC считался роскошью для академических языков, но с выходом Java (1995) стал нормой в промышленной разработке. Сегодня сборщик мусора есть в Java, C#, [Go](https://llmdocs.ru/wiki/go.md), Python, [JavaScript](https://llmdocs.ru/wiki/javascript.md), Ruby, Kotlin, Swift (через подсчёт ссылок) и большинстве скриптовых языков.

## Что считается мусором

Объект «жив», если до него можно добраться по ссылкам от **корней** — глобальных переменных, локальных переменных в стеках потоков, регистров. Всё остальное — мусор, даже если на него ещё ссылаются другие недоступные объекты. Сборщик не знает, понадобится ли объект в будущем, — он консервативно сохраняет всё достижимое. Поэтому утечки памяти возможны и с GC: например, когда объекты бесконечно накапливаются в глобальном кэше или списке подписчиков.

## Подсчёт ссылок

У каждого объекта есть счётчик ссылок на него; когда счётчик падает до нуля, объект немедленно освобождается.

- **Плюсы**: память освобождается сразу и предсказуемо, нет длинных пауз.
- **Минусы**: накладные расходы на каждое присваивание (особенно атомарные операции в многопоточной среде) и неспособность собрать **циклы** — два объекта, ссылающиеся друг на друга, никогда не получат нулевой счётчик.

Так работают CPython (с дополнительным сборщиком циклов, модуль `gc`),[4] Swift и Objective-C (ARC), `shared_ptr` в C++ и `Rc`/`Arc` в Rust. В Swift и Rust циклы разрывают слабыми ссылками (`weak`).

## Трассирующие сборщики

Трассирующий сборщик периодически обходит граф объектов от корней:[1]

| Алгоритм | Как работает | Особенности |
|---|---|---|
| **Mark-sweep** | пометить достижимые, затем пройти по куче и освободить непомеченные | память фрагментируется |
| **Mark-compact** | после пометки сдвинуть живые объекты подряд | нет фрагментации, но дорогое перемещение |
| **Копирующий** | копировать живые объекты в другую половину памяти | быстрое выделение, время зависит только от живых объектов; нужна вдвое большая память |

### Поколения

Эмпирическая **гипотеза поколений**: большинство объектов умирает молодыми (временные строки, промежуточные результаты). Поэтому кучу делят на **молодое** и **старое** поколения. Молодое собирается часто и быстро копирующим сборщиком, а пережившие несколько сборок объекты переводятся в старое, которое собирается редко. На этом построены сборщики JVM, .NET и V8.[6]

### Паузы

Простой сборщик останавливает программу на время работы (**stop-the-world**). Чтобы сократить паузы, применяют:

- **инкрементальную** сборку — работа делится на маленькие порции;
- **конкурентную** — сборщик работает в отдельных потоках параллельно с программой, используя барьеры записи для отслеживания изменений;
- **параллельную** — несколько потоков сборщика во время паузы.

## Сборщики в популярных языках

- **Java (HotSpot)**: G1 — по умолчанию, баланс пауз и пропускной способности; **ZGC** и Shenandoah — паузы в доли миллисекунды даже на кучах в терабайты; Parallel GC — максимальная пропускная способность для пакетных задач.[5]
- **Go**: конкурентный трёхцветный mark-sweep без поколений и без перемещения объектов, оптимизирован на короткие паузы. Настраивается двумя параметрами: `GOGC` (насколько куча может вырасти между сборками, по умолчанию 100%) и `GOMEMLIMIT` (мягкий предел памяти).[3]
- **Python**: подсчёт ссылок плюс поколенческий сборщик циклов.
- **JavaScript (V8)**: поколенческий сборщик Orinoco с параллельной и конкурентной работой.

## Практика

- Создавайте меньше временных объектов в горячих участках кода: переиспользование буферов (`sync.Pool` в Go), предварительное выделение ёмкости коллекций.
- Следите за метриками сборщика — частотой и длительностью пауз, размером кучи — и профилируйте память (`pprof` в Go, `tracemalloc` в Python, JFR в Java).
- В контейнерах задавайте пределы памяти, понятные среде выполнения (`GOMEMLIMIT`, `-XX:MaxRAMPercentage`), иначе процесс может быть убит по OOM раньше, чем сборщик сработает.
- Не вызывайте сборку вручную (`System.gc()`, `gc.collect()`) без измерений — обычно это только мешает эвристикам.

## См. также

- [Сборка мусора vs ручное управление памятью](https://llmdocs.ru/wiki/gc-vs-manual-memory.md)
- [Структуры данных](https://llmdocs.ru/wiki/data-structures.md)
- [Go (язык)](https://llmdocs.ru/wiki/go.md)
- [CPython](https://llmdocs.ru/wiki/cpython.md)

## Источники

1. [Jones, Hosking, Moss — The Garbage Collection Handbook (2nd ed., 2023)](https://gchandbook.org/)
2. [McCarthy — Recursive Functions of Symbolic Expressions (1960)](https://dl.acm.org/doi/10.1145/367177.367199)
3. [Go — A Guide to the Go Garbage Collector](https://go.dev/doc/gc-guide)
4. [Python Docs — gc module](https://docs.python.org/3/library/gc.html)
5. [Oracle — HotSpot Virtual Machine Garbage Collection Tuning Guide](https://docs.oracle.com/en/java/javase/21/gctuning/)
6. [V8 Blog — Trash talk: the Orinoco garbage collector](https://v8.dev/blog/trash-talk)
