В этой статье мы увидим:

  • почему сборщик мусора важен!
  • Алгоритмы сборщика мусора V8 (Scavenge, Mark / sweep / compact)
  • Новая оптимизация сборки мусора в V8 (параллельная, параллельная,…)

Почему сборщик мусора?

Раньше, когда вы писали программу, вы делали странные вещи, например, malloc или free для управления памятью вашей программы.

В современных языках сборщик мусора (GC) выполняет все это за вас. То же самое верно и для NodeJS, который использует V8 для GC и выполняет весь код javascript.

Четкое понимание механизмов, лежащих в основе GC, может помочь вам избежать ужасного кошмара разработчика; утечки памяти. Кроме того, мониторинг ГХ может помочь вам оптимизировать ваше приложение.

Почему вы должны заботиться о метриках GC?

Я работаю во французской компании Voodoo, которая создает мобильные видеоигры. У нас много проблем с производительностью, доступностью и масштабируемостью из-за безумного объема трафика, поддерживаемого нашей инфраструктурой (миллиарды событий / запросов в день… Без шуток!). В этой ситуации важен каждый аспект веб-разработки, и каждое решение может иметь большое влияние на наш бизнес.

В большинстве случаев, работая над API или проектом с низким трафиком, мы не осознаем, сколько настроек или улучшений мы можем сделать. С другой стороны, когда трафик увеличивается, все предыдущие упущения могут дорого обойтись.

В 2009 году Amazon обнаружил, что каждые 100 миллисекунд задержки обходятся им в 1% продаж.

Таким образом, хорошее понимание механизмов GC с хорошим мониторингом его показателей может помочь вам создать более надежное приложение, особенно с проектами с высоким уровнем трафика.

Алгоритм GC

Сборщик мусора не обязательно очищает память каждый раз одинаково. В V8 есть 2 основных алгоритма: Scavenge и Mark / Sweep / Compact.

Мусор

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

Малый алгоритм сборки мусора. Его используют только в новом пространстве.

Как показано на рисунке выше, цикл очистки в основном копирует память из пространства «в» в пространство «из», а затем перемещает все «живые» объекты обратно в пространство «в». Если некоторые объекты слишком старые, они перемещаются в «старое место».

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

Отметить / развернуть / компактный

Этот алгоритм основан на простом подходе: пометьте объекты в памяти, чтобы знать, какие из них еще «живы», затем очистите память, чтобы очистить «мертвые» (неиспользуемые) объекты. И, наконец, компактная память для ее оптимизации.

Отметить этап:

Рекурсивная процедура пометки достижимых объектов!

V8 использует систему маркировки белый / серый / черный.

В основном эту систему маркировки можно резюмировать так:

  • белый: исходное состояние, этот объект еще не обнаружен
  • серый: объект был обнаружен
  • черный: объект и все его соседи обнаружены

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

Вначале все «корневые» узлы будут автоматически помечены как серые, потому что GC уже знает о них. В мире NodeJs корневой узел обычно является «глобальным» объектом или «процессом».

Когда GC обнаружит все узлы, они все будут отмечены черным …… или нет. Все оставшиеся «белые» объекты будут удалены во время фазы развертки.

Фаза развертки

Удалите все неиспользуемые (белые) предметы.

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

Компактная фаза

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

Очередная оптимизация памяти! Сборщик мусора не только удаляет неиспользуемые объекты, но также пытается организовать память, чтобы обеспечить более быстрое чтение из памяти.

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

Что нового в Onirocco?

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

Итак, вот как команда V8 справилась с этим.

Во-первых, введите «инкрементную маркировку».

Фаза «отметки» занимает слишком много времени? Тогда разделите это!

Сборщик мусора разделяет работу по маркировке на более мелкие части.

Конечно, общее время всего цикла сборки мусора остается прежним, но основной поток чаще доступен для выполнения вашего кода. Это дает ощущение повышенной плавности выполнения вашей программы.

Ленивое подметание

А как насчет фазы развертки? ... .. ну, это то же самое, но это называется ленивое подметание!

Листайте страницы по мере необходимости, пока не пройдут все страницы

Фаза развертки может быть выполнена немедленно… или может быть отложена. Если объема свободной памяти достаточно для выполнения кода, зачем немедленно выполнять фазу полной развертки?

Это также можно сделать параллельно. Посмотрим как!

Одновременный

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

Но вы все еще видите здесь своего рода принцип «останови мир». Есть ли способ избавиться от этого? Конечно! И это называется «параллельным».

Параллельный

По возможности V8 может выполнять операции GC параллельно с вашим кодом.

Что может V8? Все это :)

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

Как контролировать память и сборщик мусора?

Модуль V8

Модуль v8 предоставляет API-интерфейсы, относящиеся к версии V8, встроенной в двоичный файл Node.js.

В этом модуле есть 2 важных метода:

  • v8.getHeapStatistics (): обзор глобальной памяти
  • v8.getHeapSpaceStatistics (): память, разбитая по пространству (новая, старая,…)

Основной вывод:

модуль gc-stats

Предоставляет статистику о V8 GC после его выполнения.

Во-первых, этот модуль не является встроенным в nodejs. Значит вам нужно его установить.

npm install gc-stats

Этот модуль позволяет вам прослушивать специальные события «stats» для получения данных о циклах сборки мусора.

Варианты V8

V8 может распечатать ценную информацию о своем состоянии и особенно о том, как работает сборщик мусора. Чтобы отобразить эти дополнительные данные, вы должны передать аргументы V8 (то есть NodeJs) перед запуском вашего приложения.

node --trace_gc app.js

Один из самых интересных вариантов получения данных GC - это --trace_gc. Типичный вывод должен выглядеть так:

Наконец, вы можете найти полный список опций V8 здесь:

https://gist.github.com/listochkin/10973974

APM

И последнее, но не менее важное: никогда не забывайте использовать apm (Application Performance Management) для отслеживания использования памяти в реальном времени.

Некоторые из них также могут отображать несколько показателей сборщика мусора, см. Изображение ниже.

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

Заключение

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

Также рекомендуется отслеживать и настраивать некоторые автоматические сигналы тревоги для метрик вашего GC, например, средней продолжительности цикла GC (которая не должна превышать нескольких миллисекунд) или частоты паузы GC.

Вы можете найти мой оригинальный доклад здесь: https://docs.google.com/presentation/d/17PXOZXBgdJHqBZlbULHg70_hVFLrNx9T34rB9bLDn2M/edit#slide=id.g3ffed3592f_0_0

Большое спасибо Кайлу Ченгу за исправление моего английского и моих неудобных оборотов.

Источники

Обзор Gc

Технический блог V8

Алгоритмы GC

Список опций V8