Memory management
Низкоуровневые языки программирования (например, C) имеют низкоуровневые примитивы для управления памятью, такие как malloc()
и free()
. В JavaScript же память выделяется динамически при создании сущностей (т.е. объектов, строк и т.п.) и "автоматически" освобождается, когда они больше не используются. Последний процесс называется сборкой мусора. Слово "автоматически" является источником путаницы и зачастую создает у программистов на JavaScript (и других высокоуровневых языках) ложное ощущение, что они могут не заботиться об управлении памятью.
Жизненный цикл памяти
Независимо от языка программирования, жизненный цикл памяти практически всегда один и тот же:
Выделение необходимой памяти.
Ее использование (чтение, запись).
Освобождение выделенной памяти, когда в ней более нет необходимости.
Первые два пункта осуществляются явным образом (т.е. непосредственно программистом) во всех языках программирования. Третий пункт осуществляется явным образом в низкоуровневых языках, но в большинстве высокоуровневых языков, в том числе и в JavaScript, осуществляется автоматически.
Выделение памяти в JavaScript
Выделение памяти при инициализации значений переменных
Чтобы не утруждать программиста заботой о низкоуровневых операциях выделения памяти, интерпретатор JavaScript динамически выделяет необходимую память при объявлении переменных:
Выделение памяти при вызовах функций
Вызовы некоторых функций также ведут к выделению памяти под объект:
Некоторые методы выделяют память для новых значений или объектов:
Использование значений
"Использование значений", как правило, означает чтение и запись значений из/в выделенной для них области памяти. Это происходит при чтении или записи значения какой-либо переменной или свойства объекта, или даже при передаче аргумента функции.
Освобождение памяти, когда она больше не нужна
Именно на этом этапе появляется большинство проблем из области "управления памятью". Наиболее сложной задачей в данном случае является четкое определение того момента, когда "выделенная память более не нужна". Зачастую программист сам должен определить, что в данном месте программы данная часть памяти более уже не нужна, и освободить её.
Интерпретаторы языков высокого уровня снабжаются встроенным программным обеспечением под названием "сборщик мусора", задачей которого является следить за выделением и использованием памяти, и при необходимости автоматически освобождать более ненужные участки памяти. Это происходит весьма приблизительно, так как основная проблема точного определения того момента, когда какая-либо часть памяти более не нужна - неразрешима (т.е. данная проблема не поддается однозначному алгоритмическому решению).
Сборка мусора
Как уже упоминалось выше, проблема точного определения, когда какая-либо часть памяти "более не нужна" - однозначно неразрешима. В результате сборщики мусора решают поставленную задачу лишь частично. В этом разделе мы объясним основополагающие моменты, необходимые для понимания принципа действия основных алгоритмов сборки мусора и их ограничений.
Ссылки
Большая часть алгоритмов сборки мусора основана на понятииссылки. В контексте управления памятью объект считается ссылающимся на другой объект, если у первого есть доступ ко второму (неважно - явный или неявный). К примеру, каждый объект JavaScript имеет ссылку на свойпрототип(неявная ссылка) и ссылки на значения своих полей (явные ссылки).
В данном контексте понятие "объект" понимается несколько шире, нежели для типичных JavaScript-объектов и дополнительно включает в себя понятие областей видимости функций (или глобальной лексической области)
Сборка мусора на основе подсчёта ссылок
Это наиболее примитивный алгоритм сборки мусора, сужающий понятие "объект более не нужен" до "для данного объекта более нет ни одного объекта, ссылающегося на него". Объект считается подлежащим уничтожению сборщиком мусора, если количество ссылок на него равно нулю.
Ограничение : циклические ссылки
Основное ограничение данного наивного алгоритма заключается в том, что если два объекта ссылаются друг на друга (создавая таким образом циклическую ссылку), они не могут быть уничтожены сборщиком мусора, даже если "более не нужны".
Создаётся два ссылающихся друг на друга объекта, что порождает циклическую ссылку. Они не будут удалены из области видимости функции после завершения работы этой функции, таким образом, сборщик мусора не сможет их удалить, несмотря на их очевидную ненужность. Так как сборщик мусора считает, что, раз на каждый из объектов существует как минимум одна ссылка, то уничтожать их нельзя.
Алгоритм "Mark-and-sweep"
Данный алгоритм сужает понятие "объект более не нужен" до "объект недоступен".
Основывается на понятии о наборе объектов, называемых roots(в JavaScript root'ом является глобальный объект). Сборщик мусора периодически запускается из этих roots, сначала находя все объекты, на которые есть ссылки из roots, затем все объекты, на которые есть ссылки из найденных и так далее. Стартуя из roots, сборщик мусора, таким образом, находит все доступные объекты и уничтожает недоступные.
Данный алгоритм лучше предыдущего, поскольку "ноль ссылок на объект" всегда входит в понятие "объект недоступен". Обратное же - неверно, как мы только что видели выше на примере циклических ссылок.
Начиная с 2012 года, все современные веб-браузеры оснащаются сборщиками мусора, работающими исключительно по принципу mark-and-sweep ("пометь и выброси"). Все усовершенствования в области сборки мусора в интерпретаторах JavaScript (генеалогическая/инкрементальная/конкурентная/параллельная сборка мусора) за последние несколько лет представляют собой усовершенствования данного алгоритма, но не новые алгоритмы сборки мусора, поскольку дальнейшее сужение понятия "объект более не нужен" не представляется возможным.
Теперь циклические ссылки - не проблема
В вышеприведённом первом примере после возврата из функции оба объекта не имеют на себя никаких ссылок, доступных из глобального объекта. Соответственно, сборщик мусора пометит их как недоступные и затем удалит.
То же самое касается и второго примера. Как только div и его обработчик станут недоступны из roots, они оба будут уничтожены сборщиком мусора, несмотря на наличие циклических ссылок друг на друга.
Ограничение: некоторые объекты нуждаются в явном признаке недоступности
Хотя этот частный случай и расценивается, как ограничение, но на практике он встречается крайне редко, поэтому, в большинстве случаев, Вам не нужно беспокоиться о сборке мусора.
Last updated