Народ, всем привет. Когда вы пишете программу, она работает с какими-то данными, числами, текстом, списками, объектами. и пусть мы этого не замечаем (а кто-то даже и не задумывается), но все эти данные где-то хранятся, и чаще всего в оперативной памяти. Программе нужно «откладывать» память под эти данные, а потом, когда данные больше не нужны, освобождать эту память. Если этого не делать, компьютер со временем начнёт «захлёбываться» от ненужных программе данных, ее работа замедлится, а в худшем случае — всё зависнет или сломается.
Да, я знаю, что современные компьютеры выдерживают большие потоки данных, завышенные требования к памяти и все такое прочее. Но когда вы начинаете писать большие и сложные программы, алгоритмы и прочее, задержки в выполнении программы становятся заметными. А настоящий программист должен делать все «легко» (шутка).
Но все же, чтобы избежать таких проблем, неиспользуемые данные надо убирать из памяти. Раньше за этим следили вручную, а сейчас придумали сборщики мусора — специальный механизм, который автоматически следит за памятью, освобождает то, что больше не нужно, и делает так, чтобы всё работало стабильно и быстро. Давайте сегодня разберёмся подробнее, что это такое, как работает сборщик мусора и зачем он вообще нужен. Я постараюсь простыми словами, но тут уж как пойдет.
Что происходит в памяти программы
Когда программа запускается, ей выделяется область в оперативной памяти. В этой области размещаются переменные, создаются объекты и сохраняются данные, с которыми работает программа. Ну например, вы написали такой код на языке Python:
name = "Алексей"
greeting = "Привет, " + name
Ничего сложного, здесь переменной name выделяется память для хранения строки "Алексей", а переменной greeting — для "Привет, Алексей". Всё это находится в оперативной памяти.
Но если вы потом где-то дальше по ходу программы больше не используете name или greeting, то, даже не подозревая об этом, эти переменные всё ещё будут занимать место. Ну, конечно, если специально их не удалить. В языках вроде C или C++ программист сам обязан сказать: «всё, эта память больше не нужна, освободи её». Это сложно, можно ошибиться, забыть освободить (утечка памяти) или освободить слишком рано (ошибка, ведущая к сбоям). А вот в языках вроде Java, Python, C#, JavaScript есть сборщик мусора, который делает это за программиста.
Хотите знать больше? Читайте нас в нашем Telegram – там еще больше интересного: новинки гаджетов, технологии, AI, фишки программистов, примеры дизайна и маркетинга.
Кто ты, сборщик мусора?
Сам по себе, сборщик мусора это часть среды выполнения программы, некая надстройка или часть скрытого кода, которая и следит за памятью. Она знает, какие объекты были созданы, и кто ими пользуется, определяет, какие объекты больше не нужны. Если к какому-то объекту нет ни одной «ссылки» (проще говоря, если вы никак не можете к нему обратиться), значит, он больше не нужен. Все логично.
И он как-бы в режиме реального времени просто постоянно отслеживает все эти зависимости и по необходимости, освобождает память. Таким образом, сборщик мусора помогает не забивать оперативную память тем, что больше не используется. Но как он понимает, что «уже не нужно»?
Как я уже сказал, самый простой способ — это анализ ссылок. Каждый объект в памяти может быть доступен через переменные, поля классов, элементы списков и так далее. Сборщик мусора отслеживает, какие объекты на что ссылаются и если на объект больше никто не ссылается, значит, он не нужен — и его можно удалить. Давайте для примера на всем том же python:
def create_user():
user = {"name": "Ольга"}
return user
person = create_user()
Здесь user — это словарь (объект в памяти). Пока переменная person ссылается на него, объект нужен. Но если мы потом сделаем что-то по типу person = None, то теперь нет ни одной ссылки на объект {"name": "Ольга"} — и сборщик мусора его рано или поздно удалит.
Какие бывают алгоритмы сборки мусора
1. Счётчик ссылок
Каждый объект хранит число — сколько на него сейчас ссылаются. Когда ссылка появляется — счётчик увеличивается. Когда ссылка исчезает — уменьшается. Если счётчик стал ноль — можно удалять объект. Просто и быстро, единственный минус - плохо работает с «кольцевыми» ссылками. Ну, например, для простоты:
a = {}
b = {}
a["b"] = b
b["a"] = a
Они ссылаются друг на друга, и счётчики никогда не станут нулями, хотя программа уже не использует ни a, ни b.
2. Трассировка графа объектов
Тут будет сложно, если вы не знаете, что такое графы, деревья и т.д. (а это нужно изучать алгоритмы). Сборщик мусора отмечает все объекты, к которым можно добраться от «корней» (главных переменных программы). Остальные — «мусор», их можно удалить. Этот способ уже удаляет даже кольцевые ссылки, но работает чуть медленнее, и иногда тормозит выполнение, потому что делает паузу на проверку.
3. Поколенческая сборка
Объекты делятся на «молодые» и «старые». Считается, что большинство объектов быстро умирают, поэтому часто проверяют только молодые объекты, а старые — реже. По своей сути очень эффективный алгоритм, подходит для большинства программ и используется во многих современных языках.
4. Сборка с перемещением
Больше не алгоритм, сколько дополнение. После удаления ненужных объектов, память может быть еще и фрагментированной… ну как полка, на которой пустые ячейки в разных местах. Такой сборщик сжимает объекты и уплотняет память, чтобы она была более эффективной.
Python использует сочетание счётчика ссылок и трассировки. Он также умеет находить кольцевые ссылки. Java и C# используют поколенческую сборку мусора, а JavaScript применяет трассировку объектов. Нужно ли программисту сейчас думать о сборке мусора? Вообще да, и хотя сборщик мусора работает автоматически, понимать, как он устроен — важно. Это помогает писать более эффективный код и избегать проблем. Скажем, не создавать слишком много временных объектов без нужды или не держать лишние ссылки на объекты, которые больше не нужны.
Если Вам нравятся наши статьи, и вы хотите отблагодарить автора (на развитие канала), нам будет очень приятно!