Погружение в управление памятью Javascript

В моей предыдущей статье мы говорили о том, что такое Javascript. В этой статье я хочу углубиться и начать изучение того, как работает Javascript, сосредоточившись на самом известном движке Javascript: Google V8.

«Движок» — это автономный, но управляемый извне фрагмент кода, который инкапсулирует мощную логику, предназначенную для выполнения определенного типа работы.

Каждый основной браузер поставляется с одним уже встроенным…

  • Firefox использует SpiderMonkey (первый в истории движок Javascript)
  • Safari использует JavascriptCore
  • Edge раньше использовал Chakra (теперь использует Blink и V8)

и так далее…

В этой серии я расскажу о V8, а сегодня я расскажу о сборке мусора.

Но сначала краткое введение.

Что это за штука V8?

Ссылаясь на официальные документы:

V8 — это высокопроизводительный движок JavaScript и WebAssembly с открытым исходным кодом от Google.

Он написан на C++ и встроен в проекты с открытым исходным кодом, такие как NodeJs и Deno.

Это то, что предоставляет все типы данных, операторы, объекты и функции.
Это то, что управляет памятью программы Javascript.
Это то, что оптимизирует код для более быстрой работы.
Это что делает то, что вы пишете, происходит.

💡 — Знаете ли вы, что можете получить доступ к нативному синтаксису V8 в NodeJ, передав флаг — allow-natives-syntax-flag?

Открытый источник

Исходный код V8 может скачать, изучить и улучшить каждый. В документах также есть раздел для всех, кто хотел бы внести свой вклад в разработку.

Создать из исходного кода
Зеркальное репозиторий
Добавить

Реализует ECMAScript

Что это вообще означает?
Что такое ECMAScript?
В статье, где я объяснял, что такое Javascript, я упомянул, что Javascript соответствует спецификации ECMAScript.

ECMA (Европейская ассоциация производителей компьютеров) — это ассоциация, разрабатывающая стандарты для технологий. ECMA-262 — это стандарт, содержащий спецификацию языка сценариев общего назначения. Эта спецификация называется ECMAScript.

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

💡 — Сценарии против программирования

Управляет памятью Javascript

Жизненный цикл памяти для всех языков программирования можно резюмировать следующим образом:

Распределение → Использование → Выпуск

Некоторые языки требуют ручного выделения и освобождения памяти.

int* ptr;
ptr = new int; // Dynamically allocating memory for an int variable
*ptr = 12;
cout << *ptr << endl;
delete ptr; // Deallocate memory

Джаваскрипт нет.

Итак, как, черт возьми, Javascript обрабатывает память?

Куча и стек

Механизмы Javascript имеют два места для хранения данных: куча и стек.

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

Все переменные сначала указывают на стек. Если это не примитивное значение, стек содержит ссылку, указывающую на объект в куче.

💡 — Почему я могу помещать элементы в const Arrays?

Уборщик мусора

Когда мы пишем наш код и создаем примитивы, объекты, функции, мы только что узнали, что это требует памяти. Итак, когда выполняется очистка?
В Javascript движок делает всю тяжелую работу за нас.

Способ, которым V8 достигает этого, заключается в реализации некоторых передовых концепций.

Доступность

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

Маркировка

Маркировка — это процесс, с помощью которого находят достижимые объекты.

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

подметание

Очистка — это процесс, при котором пробелы в памяти, оставленные мертвыми объектами, добавляются в структуру данных, называемую свободным списком.

Свободный список — это структура данных, обычно используемая для динамического выделения памяти, которая соединяет нераспределенные области памяти вместе в связанный список.
Когда новые данные должны быть выделенным, V8 просматривает список свободных мест, чтобы найти подходящий участок памяти.

уплотнение

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

Хотя вам не нужно каждый раз беспокоиться об освобождении памяти, это не освобождает вас от тщательной проверки вашего кода.
Такие вещи, как плохо реализованная рекурсия или бесконечный цикл, могут довольно быстро уничтожить вашу доступную память и/или привести к более серьезным последствиям. утечки памяти.

Поколения

V8 разбивает кучу на разные регионы, называемые Поколениями.

  • Молодое поколение — разделите его на Детский и Средний
  • Старое поколение

Начиная с питомника, если объект пережил сборку мусора, он сначала перемещается в промежуточное поколение, а затем , после очередного выживания до Old Generation.

Но почему?

Большинство объектов умирают молодыми

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

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

Orinoco — это кодовое название проекта Garbage Collector для использования новейших и лучших параллельных, инкрементных и параллельных методов сборки мусора, чтобы освободить основной поток.

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

В традиционных сборщиках мусора Stop The World это время может накапливаться и мешать работе пользователя (задержка или плохой рендеринг), поэтому Orinoco представила 3 ​​метода, чтобы сделать сборку более мощной.

  1. Параллельный: основной поток и вспомогательные потоки делят работу почти поровну. Это по-прежнему подход stop the world, но время паузы теперь делится на количество участвующих потоков. Javascript не запущен.
  2. Инкрементальный: основной поток не выполняет всю сборку мусора, а только периодически выполняет небольшой объем работы. Это сложнее, потому что Javascript выполняется между каждым сегментом, что означает изменение кучи и, таким образом, создает риск признания недействительной всей предыдущей работы, выполненной сборщиком мусора.
  3. Параллельный: основной поток выполняет Javascript без перерывов, в то время как вспомогательный поток выполняет сборку мусора в фоновом режиме. Это имеет тот же риск, что и метод инкрементного GC, плюс постоянные гонки между основным и вспомогательным потоком для чтения/записи объектов.

Остановивший мир, точный сборщик мусора V8 является одним из ключей к производительности V8.

Заключение

Уф… Это много для переваривания!
Я не буду углубляться в то, как сборка мусора реализована в V8, потому что это огромная и сложная тема, и одной статьи будет недостаточно. Я представил основные концепции, лежащие в основе V8, и то, как он позволяет нам писать код, не слишком задумываясь об управлении памятью (однако всегда помня о лучших практиках). V8 — это безумно огромная технология, в которой реализовано множество умных концепций, и мне не терпится раскрыть их одну за другой.
Увидимся в следующей статье.

Рекомендации

Как работает Javascript
Документация V8
Память Javascript
Управление памятью