Сборщик мусора в V8, иллюстрированное руководство

Перевод статьи Ирины Шестак. Garbage collection in V8, an illustrated guide. Опубликовано с разрешения автора.

Это руководство отличается от тех, что я делала раньше, и содержит немного сопровождающего текста вместе с эскизами. Я подумала, что вся концепция сборки мусора и то, как она обрабатывается в javascript (или, точнее, в движках javascript), заслуживает более подробного объяснения. Я также хотела бы упомянуть, что это руководство предназначено для начинающих пользователей и не охватывает все аспекты управления памятью в V8 и другие внутренности V8. Если же вы хотите исследовать глубже, то я в конце статья приложила ссылки на несколько полезных ресурсов. Также, это руководство сосредоточено на ✨javascript✨ и, очевидно, что сборка мусора в нём отличается от сборки мусора в других языках, таких, как, например, C (примечание переводчика: в C/C++ нет автоматических сборщиков мусора).

Хорошо, давайте приступим.

Что такое v8?

V8, движок javascript (не путайте с вашим любимым томатным соком 🍹), компилирует и исполняет ваш прекрасно написанный javascript. V8 поставляется с основанным на поколениях (generational), останавливающем-весь-мир (stop-the-world) сборщиком мусора (это я постараюсь объяснить ниже). Он поставляется с Chrome. Эквивалент от Mozilla это SpiderMonkey, а Chakra — от Microsoft. В любом случае, при запуске javascript вам нужен движок для его исполнения, и V8 — один из ваших вариантов, будь то в браузере или в среде node.js. (P.S. и всё это ✨опенсорс✨.)

Что такое сборщик мусора?

Важнейшей задачей сборки мусора является возможность управления использованием памяти конкретной программой. Такие языки, как C, обычно могут подключаться к управлению памятью программы и выделять и освобождать её в контексте программы. ECMAScript, с другой стороны, не имеет интерфейса для доступа к управлению памятью (да, это означает отсутствие соответствующего API). Что, в общем-то, означает, что все права на управление памятью™ в программе передаются V8.

Поскольку у нас нет доступа к бесконечному объему памяти, работа сборщика мусора заключается в том, что нужно перебрать все объекты, для которых выделена память, и определить, мертвы они или нет. Те, которые живы, должны остаться в памяти, те, которые мертвы, удаляются, а память возвращается обратно в кучу.

Что такое куча? Куча — это неструктурированная область, из которой объекты получают выделенную память. Распределение является динамическим, поскольку размер / время жизни / количество объектов неизвестно, поэтому выделение и освобождение памяти происходит во время выполнения программы.

Поэтому, если мы посмотрим на конкурентную модель, куча работает непосредственно со стеком вызовов, так как объекты, отображаемые в стеке, требуют выделения памяти. Это будет выглядеть примерно так:

Живой или мёртвый?

Базовая проверка того, жив или мертв объект, основана на том, может ли клиент или программа, выполняющая код, достичь его. Наиболее достижимый объект, как вы можете подумать, это, вероятно, объект, объявленный в корневой области видимости.

Некоторые C++ биндинги (или веб-API на клиенте) также являются частью корня, поэтому вы можете напрямую обращаться к методам типа setInterval.

О достижимости можно также думать так: сможет ли другой объект или корневой объект получить его, и если да, то память, требуемая для этого объекта, сохраняется.

Итак, как мы можем добраться до сборки мусора? (Расскажи мне! Расскажи мне!)

V8 выделяет память в куче при создании новых объектов или новых «указателей». (В javascript нет настоящих указателей, поэтому «указатели» технически просто копируют ссылки на исходный объект). В куче есть множество разных пространств для разных типов объектов, и она будет организована примерно так:

В целях сбора мусора V8 делит кучу на две части: молодое и старое пространство. Когда вы выполняете операции, требующие выделения памяти, V8 выделяет место в первой части. Когда вы продолжаете добавлять вещи в кучу, вы в конечном итоге исчерпываете память, поэтому V8 будет запускать очистку. Для вновь созданных объектов память выделяется очень быстро из короткой и быстрой коллекции, очищаемой на регулярной основе (для удаления уже мертвых объектов). Как только объекты «переживают» несколько (2, чтобы быть точными) циклов очистки, они переносятся в старое пространство, обрабатывающееся сборщиком мусора в отдельном цикле по факту заполнения.

Старые объекты — это те, которые пережили более одного цикла сборки мусора, то есть на них продолжают ссылаться другие объекты, и они должны оставаться в памяти. Обычно они не ссылаются на молодые объекты, но продолжают иметь ссылки на более старые объекты. Нежелательно допускать смешение поколений, так как это делает уборку менее чистой.

🆒 sources.js

  • memory management; мне нравится читать глоссарий, потому что вы обнаруживаете кучу замечательных вещей.
  • это действительно хороший и детальный справочник по производительности v8.
  • вы можете заглянуть в v8 wiki для получения большей информации о внутренней работе v8 и том, как отлаживать ваши проекты.
  • о “частоте кадров” в firefox dev tools
  • другое хорошее руководство V8 && garbage collection

Эта статья на GitHub


Читайте нас на Медиуме, контрибьютьте на Гитхабе, общайтесь в группе Телеграма, следите в Твиттере и канале Телеграма, скоро подъедет подкаст. Не теряйтесь.