Что такое сборка мусора в JavaScript?

Ответы:

192

Эрик Липперт написал подробное сообщение в блоге на эту тему некоторое время назад (дополнительно сравнивая его с VBScript ). Точнее, он написал о JScript , который является собственной реализацией ECMAScript от Microsoft, хотя и очень похож на JavaScript. Я полагаю, что вы можете предположить, что подавляющее большинство поведения будет таким же для движка JavaScript Internet Explorer. Конечно, реализация будет отличаться от браузера к браузеру, хотя я подозреваю, что вы могли бы взять ряд общих принципов и применить их к другим браузерам.

Цитируется с этой страницы:

В JScript используется сборщик мусора с разметкой разметки. Это работает так:

  • Каждая переменная, которая находится "в области видимости", называется "мусорщиком". Мусорщик может ссылаться на число, объект, строку, что угодно. Мы поддерживаем список мусорщиков - переменные перемещаются в список scav, когда они входят в область видимости, и из списка scav, когда они выходят из области видимости.

  • Время от времени работает сборщик мусора. Сначала он ставит «метку» на каждый объект, переменную, строку и т. Д. - всю память, отслеживаемую GC. (JScript использует внутреннюю структуру данных VARIANT, и в этой структуре есть множество дополнительных неиспользуемых битов, поэтому мы просто устанавливаем один из них.)

  • Во-вторых, он очищает отметку на мусорщиках и переходное закрытие ссылок мусорщиков. Таким образом, если объект мусорщика ссылается на объект некавенджера, то мы очищаем биты на некавенгере и на всем, на что он ссылается. (Я использую слово «закрытие» в другом смысле, чем в моем предыдущем посте.)

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

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

Историческая справка: более ранняя редакция ответа содержала неверную ссылку на deleteоператора. В JavaScript оператор удаляет свойство из объекта , и полностью отличается в C / C ++.deletedelete

нолдорин
источник
27
руководство Apple имеет недостатки: автор использует deleteнеправильно; например, в первом примере вместо delete foo, вы должны сначала удалить прослушиватель событий через, window.removeEventListener()а затем использовать foo = nullдля перезаписи переменной; в IE delete window.foo(но не delete foo) также работало бы, если бы оно fooбыло глобальным, но даже тогда это не сработало бы в FF или Opera
Кристоф
3
Имейте в виду, что статью Эрика следует рассматривать «только в исторических целях». Но это все еще информативно.
Петр Иван
2
Также обратите внимание: в IE 6 и 7 НЕ используется сборщик мусора с разметкой разметки. Они используют простой сборщик мусора с подсчетом ссылок, который более уязвим для циклических проблем со сборкой мусора.
Дуг
1
ECMAScript delete- это унарный оператор (выражение), а не оператор (то есть:) delete 0, delete 0, delete 3. Это выглядит как выражение, когда оно выражено выражением выражения.
Гидропер
Да, ответ на данный момент уже устарел, так как с 2012 года современные браузеры используют алгоритм mark / sweep ... так что он больше не зависит от объема. Ссылка: developer.mozilla.org/en-US/docs/Web/JavaScript/…
sksallaj
52

Остерегайтесь циклических ссылок, когда задействованы объекты DOM:

Шаблоны утечки памяти в JavaScript

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

Вот простой пример:

function init() {
    var bigString = new Array(1000).join('xxx');
    var foo = document.getElementById('foo');
    foo.onclick = function() {
        // this might create a closure over `bigString`,
        // even if `bigString` isn't referenced anywhere!
    };
}

Наивная реализация JS не может собирать, bigStringпока существует обработчик событий. Есть несколько способов решить эту проблему, например, установка bigString = nullв конце init()( deleteне будет работать для локальных переменных и аргументов функций: deleteудаляет свойства из объектов, а объект переменной недоступен - ES5 в строгом режиме даже выдаст a, ReferenceErrorесли вы попытаетесь удалить локальную переменную!).

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

Christoph
источник
20
Ошибка круговой ссылки DOM характерна для JScript - ни один другой браузер не страдает от этого, кроме IE. На самом деле я совершенно уверен, что спецификация ECMAScript явно заявляет, что GC должен уметь обрабатывать такие циклы: - /
olliej
@olliej: Я не вижу упоминания о GC в спецификации ECMAScript .
Янус Троелсен
см. также point.davidglasser.net/2013/06/27/…
Кристоф
16

Хорошая цитата взята из блога

Компонент DOM является «сборщиком мусора», как и компонент JScript, что означает, что если вы создадите объект в любом из компонентов, а затем потеряете отслеживание этого объекта, он в конечном итоге будет очищен.

Например:

function makeABigObject() {
var bigArray = new Array(20000);
}

Когда вы вызываете эту функцию, компонент JScript создает объект (с именем bigArray), который доступен внутри функции. Однако, как только функция возвращается, вы теряете отслеживание bigArray, потому что больше нет возможности ссылаться на него. Ну, компонент JScript понимает, что вы потеряли его, и поэтому bigArray очищается - его память освобождается. То же самое работает в компоненте DOM. Если вы говорите document.createElement('div'), или что-то подобное, то компонент DOM создает объект для вас. Как только вы как-то потеряете отслеживание этого объекта, компонент DOM очистит связанный.

TStamper
источник
13

Насколько я знаю, объекты JavaScript периодически собираются, когда на объект не остается ссылок. Это происходит автоматически, но если вы хотите узнать больше о том, как это работает, на уровне C ++ имеет смысл взглянуть на исходный код WebKit или V8.

Как правило, вам не нужно думать об этом, однако в старых браузерах, таких как IE 5.5 и более ранние версии IE 6, и, возможно, в текущих версиях, замыкания будут создавать циклические ссылки, которые, если их не проверять, в конечном итоге израсходуют память. В конкретном случае, когда я имею в виду замыкания, это было, когда вы добавили ссылку JavaScript на объект dom и объект на объект DOM, который ссылался на объект JavaScript. По сути, его никогда не удастся собрать, и в конечном итоге ОС станет нестабильной в тестовых приложениях, которые зацикливаются для создания сбоев. На практике эти утечки обычно невелики, но для поддержания чистоты кода необходимо удалить ссылку JavaScript на объект DOM.

Обычно хорошей идеей является использование ключевого слова delete для немедленной отмены ссылки на большие объекты, такие как данные JSON, которые вы получили назад и сделали с ними все, что вам нужно, особенно в разработке для мобильных устройств. Это приводит к следующей очистке GC, чтобы удалить этот объект и освободить его память.

Heat Miser
источник
Решена ли проблема циклических ссылок JavaScript -> DOM -> JavaScript в более новых версиях IE? Если да, то с каких это пор? Я думал, что это архитектурно очень глубоко и вряд ли когда-либо будет исправлено. Есть ли у вас источники?
erikkallen
Просто анекдотично. Я не заметил сумасшедших утечек в IE 8, работающем в режиме стандартов, а не в сломанном режиме. Я поправлю свой ответ.
Heat Miser
1
@erikkallen: да, ошибка GC была исправлена ​​в версиях IE 8+, так как более старые использовали очень наивный алгоритм сборки мусора, что делало невозможным GC пару объектов, ссылающихся друг на друга. Новые mark-and-sweepалгоритмы стиля заботятся об этом .
kumarharsh
6

сборщик мусора (GC) - это форма автоматического управления памятью, удаляющая ненужные объекты.

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

1 - выделите необходимое пространство памяти

2 - сделать некоторую обработку

3 - освободить это пространство памяти

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

Сборка мусора с подсчетом ссылок : этот алгоритм сокращает определение «объект больше не нужен» до «у объекта нет ссылок на него», объект будет удален, если на него не будет ссылки

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

В настоящее время большинство современных браузеров используют второй алгоритм.

Ахмед Габер - Бига
источник
1
И чтобы добавить источник этого, смотрите MDN: developer.mozilla.org/en-US/docs/Web/JavaScript/…
Xenos
4

«В информатике сборщик мусора (GC) - это форма автоматического управления памятью. Сборщик мусора, или просто сборщик, пытается вернуть мусор или память, используемую объектами, которые никогда не будут доступны приложению».

Все движки JavaScript имеют свои собственные сборщики мусора, и они могут отличаться. В большинстве случаев вам не нужно иметь с ними дело, потому что они просто делают то, что должны делать.

Написание лучшего кода в основном зависит от того, насколько хорошо вы знаете принципы программирования, язык и особенности реализации.

mtasic85
источник
1

Что такое сборка мусора в JavaScript?

проверить это

Что важно для веб-программиста, чтобы понять, что такое сборка мусора в JavaScript, чтобы лучше писать код?

В Javascript вы не заботитесь о выделении и освобождении памяти. Вся проблема требует интерпретатор Javascript. Утечки все еще возможны в Javascript, но они являются ошибками интерпретатора. Если вы заинтересованы в этой теме, вы можете прочитать больше на www.memorymanagement.org

DFA
источник
Какая из различных систем управления памятью в статье, на которую вы ссылаетесь, используется в JavaScript? «Утечки все еще возможны в Javascript, но они являются ошибками интерпретатора». - Это не означает, что JS-программисты могут просто игнорировать всю проблему, например, существует довольно известная проблема циклических ссылок JS <-> DOM в старых версиях IE, которую вы могли бы обойти в своем коде JS. Кроме того, способ работы JS-замыканий является конструктивной особенностью, а не ошибкой, но вы можете связать большие куски памяти, чем предполагалось, если вы используете замыкания «ненадлежащим образом» (я не говорю, что не используйте их).
nnnnnn
3
Утечки памяти - чудовище в JavaScript. Если вы пишете простое приложение «проект колледжа», то не беспокойтесь. Но когда вы начинаете писать высокопроизводительные приложения корпоративного уровня, управление памятью в JavaScript становится обязательным.
Дуг
1

В Windows вы можете использовать Drip.exe, чтобы найти утечки памяти или проверить, работает ли ваша бесплатная программа mem.

Это действительно просто, просто введите URL веб-сайта, и вы увидите потребление памяти встроенным IE рендерером. Затем нажмите обновить, если память увеличивается, вы обнаружили утечку памяти где-то на веб-странице. Но это также очень полезно, чтобы увидеть, работают ли подпрограммы для освобождения памяти для IE.

powtac
источник
1

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

var object = new Object();

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

var object1 = new Object();
var object2 = object1;

Две переменные, указывающие на один объект

JavaScript - это язык для сборки мусора , поэтому вам не нужно беспокоиться о распределении памяти при использовании ссылочных типов. Однако лучше разыменовать объекты, которые вам больше не нужны, чтобы сборщик мусора мог освободить эту память. Лучший способ сделать это - установить для переменной объекта значение null.

var object1 = new Object();
// do something
object1 = null; // dereference

Разыменование объектов особенно важно в очень больших приложениях, которые используют миллионы объектов.

из принципов объектно-ориентированного JavaScript - НИКОЛАЙ С. ЗАКАС

Ани Наслян
источник