Есть ли в Javascript утечки памяти ? Если да, то как этого избежать? Ввиду того, что в последнее время мне задавали подобные вопросы несколько человек, похоже, никто систематически не изучал эту часть контента. Поэтому я планирую поделиться с вами некоторой информацией, которую собрал несколько лет назад.
Прежде всего, можно с уверенностью сказать, что некоторые способы написания JavaScript будут вызывать утечки памяти, по крайней мере, под IE6. Поэтому сегодня, когда IE6 отказывается уходить на пенсию, нам все равно необходимо разобраться в соответствующих знаниях (хотя в большинстве случаев утечка памяти, вызванная js, не является основной причиной замедления работы компьютера). Соответствующие исследования в основном сосредоточены в 2005-07 годах. В этой статье нет никаких новых идей. Если у вас есть друзья, которые изучали ее тогда, вы можете просто проигнорировать ее.
Как фронтенд-разработчик, чтобы понять эти проблемы, вам необходимо знать, что они собой представляют и почему. Поэтому, прежде чем рассказывать об утечках памяти js, давайте начнем с того, почему возникают утечки памяти.
Говоря об утечках памяти, мы должны говорить о том, как распределяется память. Существует три способа распределения памяти, а именно:
1. Статическое распределение: форма распределения статических переменных и глобальных переменных. Если мы думаем о комнате как о программе, мы можем думать о статически выделенной памяти как о прочной мебели в комнате. Обычно их не нужно выбрасывать и перерабатывать, ведь никто каждый день не выбрасывает шкафы в окно как мусор.
2. Автоматическое выделение: метод выделения памяти для локальных переменных в стеке. Память в стеке может быть автоматически освобождена с помощью операции pop при выходе из блока кода.
Это похоже на людей, которые приходят в комнату, чтобы сделать что-то, как только все будет сделано, они уйдут сами, и занимаемое ими пространство будет автоматически освобождено, когда эти люди уйдут.
3. Динамическое распределение: метод динамического распределения памяти в куче для хранения данных. То есть память, примененную для использования malloc или new при работе программы, нам нужно самостоятельно освободить с помощью free или удалить. Срок жизни динамической памяти определяется программистом. Если вы забудете его освободить, это неизбежно приведет к утечке памяти. В этом случае блоки памяти в куче подобны салфеткам, которыми мы пользуемся каждый день. После их использования нам приходится выбрасывать их в мусорное ведро, иначе в доме будет беспорядок. Поэтому ленивые люди мечтают иметь домашнего робота, который бы мог с ними убираться. В разработке программного обеспечения, если вам лень освобождать память, то вам тоже понадобится подобный робот — который по сути является сборщиком мусора, реализованным по определенному алгоритму. Именно некоторые дефекты в самом механизме сборки мусора приводят к утечкам памяти JavaScript.
Несколько лет назад я прочитал статью «Интересная история переработки мусора», в которой подробно объяснялся механизм сбора мусора.
Точно так же, как наддув, технология, используемая во многих роскошных автомобилях в качестве аргумента в пользу продажи, которая фактически использовалась Mercedes-Benz в 1910-х годах, технология переработки мусора существует уже давно. Язык Лисп, родившийся в Массачусетском технологическом институте примерно в 1960 году, был первым языком, который в значительной степени опирался на технологию динамического распределения памяти. Почти все данные в Лиспе представлены в форме «таблиц», а пространство, занимаемое «таблицами», находится в куче. динамически распределяется. Врожденная функция динамического управления памятью языка Lisp требует от разработчиков языка Lisp решения проблемы автоматического освобождения каждого блока памяти в куче (в противном случае программисты Lisp неизбежно будут перегружены бесчисленными операторами освобождения или удаления в программе). Это непосредственно привело к зарождению и развитию технологий сбора мусора.
В то же время вместе появились и три самых основных алгоритма сборки мусора. Давайте посмотрим на них один за другим:
Алгоритм подсчета ссылок: возможно, это первый метод, который приходит на ум. Образно говоря, подсчет ссылок можно понимать так. В доме много официальных документов, и эти бумаги — как воспоминания. Использование памяти похоже на письмо на этих листах бумаги. Память можно использовать по своему желанию, но есть условие: каждый, кто использует лист бумаги, должен написать на углу листа цифру 1. Если два человека одновременно используют лист бумаги, счет становится равным. 2 и так далее. Когда человек заканчивает использовать лист бумаги, счетчик в углу должен уменьшиться на 1. Таким образом, как только счетчик станет равным 0, условия сбора мусора будут выполнены, и ожидающий в стороне робот немедленно бросит бумагу в мусор. Сборщик мусора на основе счетчика ссылок работает быстрее, не прерывает выполнение программы на длительное время и подходит для программ, которые должны выполняться в реальном времени. Однако счетчик ссылок увеличивает накладные расходы на выполнение программы; в то же время существует еще одна большая проблема. У этого алгоритма есть недостаток: как только генерируется циклическая ссылка, происходит утечка памяти. Например, мы создаем два новых объекта a и b. В этот момент счетчики a и b равны 1. Затем мы указываем атрибут a на b, а атрибут b на этот раз из-за. ссылочного отношения, счетчики a и b становятся равными 2. Когда программа завершается и выходит из области видимости, программа автоматически уменьшает счетчик a на 1. Поскольку счетчик a в конце все еще равен 1, a не будет Аналогично, окончательный счетчик b также равен 1, b не будет освобожден, и память просто утекла!