Оптимизация и понимание управления памятью в JavaScript

Оптимизация и понимание управления памятью в JavaScript

  • 23 февраля
  • читать 10 мин
Владимир Шайтан
Владимир Шайтан Senior Full Stack Developer в UKEESS Software House, Преподаватель Компьютерной школы Hillel.

Управление памятью JavaScript выполняется автоматически и незаметно для нас. Мы создаем примитивы, объекты, функции… Все это требует памяти.

Что происходит, когда что-то большее не нужно? Как механизм JavaScript это обнаруживает и очищает?

В этой статье узнаем, что такое Garbage collection (GC), для чего он нужен вообще и какие проблемы решает.

Для понимания: первый язык, который поддерживал сбор мусора - это был язык APL в 1964 году. Вы только представьте, что даже 60 лет назад люди уже думали о том, что нужно попытаться уволить время разработчиков для более полезных задач!

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

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

Основной концепцией управления памятью в JavaScript является доступность. Собиратель мусора следит за всеми объектами и удаляет ставшие недоступными.

Проще говоря, «доступные» значения — это доступные или которые можно использовать. Они гарантированно хранятся в памяти.

Какие это значения:

  • Функция, выполняемая в данный момент, ее локальные переменные и параметры.
  • Другие функции в текущей цепочке вложенных вызовов, их локальные переменные и параметры.
  • Глобальные переменные.

Рекомендуем курс по теме

Как происходит процесс уборки мусора?

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

Регулярно выполняются следующие шаги:

  • Собиратель мусора берет <root>'s и «обозначает» (т.е. запоминает) их.
  • Затем он проходит по ним и «обозначает» все ссылки, которые есть внутри.
  • Затем он проходит по уже обозначенным объектам и обозначает их ссылку. Все посещаемые объекты запоминаются, чтобы не посещать один и тот же объект дважды в будущем.
  • …И так далее, пока не будет посещена каждая доступная (от корней) ссылка.
  • Все объекты, кроме отмеченных, удаляются.

Пример для объяснения

Структура объекта выглядит так:


С правой стороны видны объекты, к которым ничего не ведет. Еще они могут называться "недоступным островом".

Первый шаг – обозначает <root>’s:

Затем мы следуем за их ссылками и отмечаем объекты, на которые они ссылаются:

Продолжаем следить за дальнейшими ссылками, пока это возможно:

Теперь объекты, по которым не удалось пройтись в процессе, считаются недоступными и будут удалены:

В сборке мусора имеется важный термин: "Гипотеза поколений". Согласно этой гипотезе, объекты программы обычно имеют разные "возрастные" характеристики: большинство объектов создаются в начале выполнения программы и быстро становятся ненужными, в то время как некоторые объекты существуют дольше. Итак, основная мысль гипотезы поколений состоит в том, что объекты можно поделить на несколько "поколений", при этом каждое поколение имеет свои свойства относительно продолжительности жизни объектов.

Поколение:

  1. молодое (young)
  2. среднее (middle)
  3. старое (old)

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

Как мы можем оптимизировать работу с памятью, помогая тем самым GC?

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

1. Пытаемся избегать утечки памяти:

  • Используем замыкания и другие механизмы управления областями видимости и памятью

2. Избегаем глубокой вложенности:

  • Это может привести к еще большему объему памяти, который нужно будет собирать.

3. Эффективное использование памяти:

  • Если это возможно, лучше избегать больших объектов и массивов, разделяя их на модули и отдельные массивы.

4. Оптимизация циклов предупреждает сверхзамену рекурсии

  • Чрезмерная рекурсия может привести к созданию многих ненужных объектов в памяти.

5. Используйте встроенные методы оптимизации работы с памятью:

  • JavaScript и другие языки программирования предоставляют встроенные методы оптимизации работы с памятью, такие как методы управления памятью типа "WeakMap" или "WeakSet".

6. Минимизировать создание ненужных объектов:

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

Вывод

Механизм сбора мусора является важной составляющей автоматического управления памятью, которое делает разработку программ более безопасной и удобной для разработчиков.

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

Эти рекомендации позволят разработчикам лучше понимать принципы работы языка и использовать его возможности максимально эффективно.

Пишите, проверяйте, учитесь и обязательно наслаждайтесь этим процессом.

Рекомендуем курс по теме