Всем привет, Хабрчане! Во время подготовки к очередному тех-собесу, я задался вопросом: а как в V8 работает Garbage Collector, что такое Heap и Stack, про которые я неоднократно слышал? После прочтения нескольких англоязычных и русскоязычных ресурсов понял, что одни копают очень глубоко, а другие дают лишь минимальную базу. Я решил сделать что-то среднее и агрегировать все эти знания в одну статью, плавно погружая читателя в тему.
Кстати, я начинающий Node.js разработчик и это моя первая статья, прошу не судить строго :)
Погружаемся!
![](https://habrastorage.org/getpro/habr/upload_files/3ae/000/cd8/3ae000cd820ad38d91e342f7dfe7b55a.jpg)
Начнем с базы
Принцип сборки мусора довольно прост: если на сегмент в памяти никто не ссылается, например на объект, можно считать, что он (объект) не используется, и очистить его. Такой принцип ещё называют "принципом достижимости".
Основной алгоритм сборки мусора называется mark-and-sweep. Этот алгоритм может быть объединен также и с алгоритмом mark-compact. Вместе они работают следующим образом:
Сборщик мусора отмечает все корневые объекты.
Далее помечаются все объекты, на которые ссылаются эти корни.
Процесс повторяется для всех достижимых объектов.
После этого непомеченные объекты считаются недостижимыми и удаляются.
Происходит перемещение (дефрагментация) оставшихся объектов. Это уменьшит фрагментацию и повысит производительность выделения памяти для новых объектов.
![mark-and-sweep + mark-compact mark-and-sweep + mark-compact](https://habrastorage.org/getpro/habr/upload_files/b11/38f/cb8/b1138fcb8839df42dbc51bd60193f59e.gif)
Существует ещё несколько алгоритмов сборки мусора. Некоторые из наиболее распространенных алгоритмов включают:
Копаем глубже
Как в памяти V8 распределяются переменные, функции и объекты? V8 использует схему, основанную на концепции Java Virtual Machine (JVM) и делит память на сегменты:
V8 memory schema
![](https://habrastorage.org/getpro/habr/upload_files/172/135/050/172135050e6b34ff46cc0ebc7c1960ec.png)
Code Segment: выполняемый на данный момент код.
Stack (статическое выделение памяти): содержит все примитивные типы данных (вроде
int
,bool
,string
) с указателями на функции, объекты, а также информация о вызовах методов.Heap (динамическое выделение памяти): сегмент памяти, предназначенный для хранения ссылочных типов данных, вроде объектов, массивов и функций. Это самый большой блок области памяти и именно здесь происходит сборка мусора.
Подробнее про каждый сегмент
Stack:
Это область памяти, выделенная для каждого процесса в V8. Здесь, как говорилось ранее, хранятся статические данные, включая фреймы методов/функций, примитивные значения и указатели на объекты. Стек работает по принципу LIFO (Last In, First Out), то есть последний добавленный элемент будет первым извлеченным.
Heap – самый большой блок области памяти, под капотом он делиться на:
Heap schema
![](https://habrastorage.org/getpro/habr/upload_files/ca4/d31/12b/ca4d3112badc8412d9452527de48e580.png)
Young generation - место, где "живут" новые объекты, и большинство из них недолговечны. Это пространство небольшое и состоит из двух полу-пространств (from-space, to-space).
-
Old generation - место, куда перемещаются объекты, которые пережили два цикла сборки мусора в Young generation блоке. Это пространство управляется основным алгоритмом сборки мусора mark-and-sweep. Old generation можно поделить ещё на два подпространства:
Old pointer space - cодержит объекты, которые пережили два цикла сборки мусора, и имеют указатели на другие объекты.
Old data space – содержит исключительно объекты, которые имеют данные (без ссылок) и строки, числа, массивы.
Large object space – тут хранятся объекты, размер которых превышает размер других пространств. Большие объекты никогда не удаляются сборщиком мусора.
Code-space - Здесь Just In Time (JIT) компилятор сохраняет скомпилированные блоки кода. Это единственное пространство с исполняемой памятью.
Cell space, property cell space, and map space – тут хранятся сервисные объекты, которые упрощают сборку мусора.
Заключение
Я надеюсь, что эта статья была полезной и интересной. Буду рад любым замечаниям и вопросам. Спасибо за внимание!
Вдохновлялся:
Мой репозиторий "Расширяем кругозор: Node.js и JavaScript", где я записываю интересные ответы на вопросы и познаю дзен V8.
Комментарии (3)
iliazeus
15.08.2024 12:17Большие объекты никогда не удаляются сборщиком мусора.
Это очевидная неправда, или, по крайней мере, сформулировано слишком уж криво. Проверить достаточно просто - запустите цикл, создающий большие массивы или
Buffer
ы, но не сохраняющий на них ссылки, и посмотрите на потребление памяти этой программой.Правда в том, что они, разумеется, удаляются сборщиком - но лежат в отдельном месте памяти, и никогда за время жизни не перемещаются. Интуиция в том, что из-за того, что эти объекты большие, перемещать их дорого. Но из-за их размера, их обычно не так много - поэтому в этом их отдельном пространстве фрагментация будет меньшей проблемой.
iliazeus
15.08.2024 12:17+1V8 использует схему, основанную на концепции Java Virtual Machine (JVM)
Упоминание JVM тоже выглядит довольно странно. Насколько я знаю, V8 и JVM - совсем не родственники, и я не уверен, насколько похоже у них устроены GC.
AccountForHabr
Чето статья закончилась не успев толком и начаться =(