Что такое сборщик мусора и почему память не убирается сама собой
Каждая программа берёт память у компьютера — но кто возвращает её обратно? В одних языках это делает невидимый уборщик, в других приходится следить вручную. Заглянем, как работает эта незаметная магия.
Программа постоянно одалживает память у компьютера — и если её никто не возвращает, рано или поздно она закончится.
Сборщик мусора — это автоматический уборщик, который находит память, до которой программа больше не может дотянуться, и возвращает её системе.
Память — это одолженный стол
Представьте память как большой рабочий стол. Когда программе нужно где-то разместить данные — список покупок, картинку, текст, — она занимает кусочек стола. Проблема в том, что стол не бесконечен. Если занимать места всё больше и никогда не освобождать, рано или поздно оно кончится, и программа упадёт с нехваткой памяти.
Значит, отработанные данные нужно убирать. Вопрос — кто это делает: человек или машина.
Ручная уборка и её опасности
В языках вроде C убирать за собой обязан программист: явно сказать «эта память больше не нужна, освободи». Звучит просто, но на практике это источник самых противных багов. Забыл освободить — память утекает, программа со временем разбухает. Освободил, но где-то ещё осталась ссылка, и по ней обратились — обращение к уже освобождённой памяти, почти гарантированный сбой. Освободил дважды — тоже беда.
Автоматическая уборка
Чтобы снять эту ношу с человека, придумали сборщик мусора (garbage collector, GC). Это часть среды выполнения языка, которая сама следит за памятью. Так работают Java, Python, JavaScript, C#, Go и многие другие — там о ручном освобождении думать почти не нужно.
Как он понимает, что мусор, а что нет
Ключевая идея — достижимость. Данные считаются нужными, пока к ним можно добраться: есть живая переменная, ссылающаяся на них, или цепочка ссылок от живых переменных. Как только последняя ссылка пропадает, дотянуться до данных уже невозможно — и они объявляются мусором.
Классический способ это вычислить — «пометить и убрать» (mark-and-sweep): сборщик стартует от корней (активных переменных), обходит все ссылки и помечает всё достижимое. Что не помечено — недостижимо, значит, мусор, и эту память можно вернуть. Есть и другой приём — подсчёт ссылок: каждое значение помнит, сколько ссылок на него указывает; счётчик упал до нуля — память свободна.
| Подход к памяти | Кто убирает | Риск утечек | Цена |
| Ручной (C) | Программист | Высокий | Сложность, баги |
| Сборщик мусора | Среда выполнения | Низкий | Ресурсы, паузы |
| Владение (Rust) | Компилятор заранее | Низкий | Сложнее писать |
Цена удобства
Сборщик мусора почти бесплатен в плане нервов, но не бесплатен по ресурсам. Ему нужно регулярно «осматривать» память, а это занимает процессорное время. Хуже того, классический сборщик иногда ненадолго останавливает программу, чтобы навести порядок, — те самые микропаузы. Для большинства приложений они незаметны, но в играх или системах реального времени даже короткий замер критичен. Поэтому современные сборщики стали очень умными, работают по частям и стараются не мешать.
Так нужен ли он
Сборщик мусора — это сделка: вы отдаёте немного производительности и контроля, а взамен получаете огромную экономию сил и защиту от целого класса ошибок. Для большинства задач это отличный обмен — недаром им оснащены самые популярные языки. А там, где каждая миллисекунда на счету, выбирают ручное управление или подход вроде Rust, где порядок наводит не уборщик во время работы, а компилятор заранее.