2015-09-30 2 views
6

Я читаю эту статью (http://javascript.info/tutorial/memory-leaks#memory-leak-size) об утечках памяти, в котором упоминается это как утечка памяти:Нужно ли вручную очищать незафиксированные переменные в закрытии?

function f() { 
    var data = "Large piece of data"; 

    function inner() { 
     return "Foo"; 
    } 

    return inner; 
} 

интерпретатор JavaScript не имеет представления о том, какие переменные могут потребоваться по внутренней функции, поэтому он держит все. В каждом внешнем LexicalEnvironment. Надеюсь, новые интерпретаторы попытаются его оптимизировать, но не уверены в их успехе.

В статье предлагается установить вручную data = null, прежде чем мы вернем внутреннюю функцию.

Сохраняется ли это сегодня? Или эта статья устарела? (Если он устарел, может кто-то указать мне на ресурс о текущих ловушках)

ответ

5

Современные двигатели не будут поддерживать неиспользованные переменные во внешней области.

Поэтому не имеет значения, если вы установили data = null перед возвратом внутренней функции, поскольку внутренняя функция не зависит от («закрыть») data.

Если внутренняя функция сделал зависит от data --perhaps возвращает его - то установка data = null конечно не то, что вы хотите, потому что тогда, ну, это было бы пустой вместо того, чтобы его первоначальное значение !

Предполагая, что внутренняя функция действительно зависит от data, то да, до тех пор, как inner в настоящее время указывает на (ссылается) что-то, то значение data должны быть вокруг. Но это то, что ты хочешь сказать! Как у вас есть что-то доступное, если оно не доступно?

Помните, что в какой-то момент переменная, которая содержит возвращаемое значение f(), сама выйдет за рамки. В этот момент, по крайней мере, до f() вызывается снова, data будет собираться мусором.

Общее правило: вам не нужно беспокоиться о памяти и утечки с помощью JavaScript. В этом весь смысл GC. Сборщик мусора делает отличную работу по определению того, что необходимо и что не нужно, и сохранению прежнего и мусора, собирающего последний.

Вы можете рассмотреть следующий пример:

function foo() { 
    var x = 1; 
    return function() { debugger; return 1; }; 
} 

function bar() { 
    var x = 1; 
    return function() { debugger; return x; }; 
} 

foo()(); 
bar()(); 

и изучить его исполнение в Chrome Devtools окно переменных. Когда отладчик останавливается во внутренней функции foo, обратите внимание, что x нет в качестве локальной переменной или как закрытие. Для всех практических целей его не существует.

Когда отладчик останавливается во внутренней функции bar, мы видим переменную x, так как она должна быть сохранена, чтобы быть доступной для возврата.

Сохраняется ли это сегодня? Или эта статья устарела?

Нет, это не так, и да, это так. Статья составляет четыре года, и это целая жизнь в мире Интернета. Я не знаю, может ли jQuery подвергаться утечкам, но я был бы удивлен, если бы это было так, и если да, то есть достаточно простой способ избежать их - не используйте jQuery. Утечки упоминаний автора статьи, связанных с циклами DOM, и обработчики событий отсутствуют в современных браузерах, и я имею в виду IE10 (скорее IE9) и выше. Я бы предложил найти более актуальную ссылку, если вы действительно хотите понять утечку памяти. На самом деле, я предлагаю вам в основном перестать беспокоиться о утечке памяти. Они происходят только в очень специализированных ситуациях. Трудно найти много на эту тему в Интернете в эти дни по этой точной причине. Вот одна статья, которую я нашел: http://point.davidglasser.net/2013/06/27/surprising-javascript-memory-leak.html.

+0

Спасибо torazaburo. Меня беспокоит, что последний фрагмент в статье, которую вы предоставили об ошибке Meteor, действительно теряет память в V8. – Jonathan

+0

«На самом деле, я бы посоветовал вам в основном перестать беспокоиться о утечке памяти. Они происходят только в очень специализированных ситуациях». Я категорически не согласен с этим утверждением. По крайней мере, отдельные DOM часто встречаются в SPA-центрах, и каждый разработчик должен знать об этом. –

1

В дополнение к превосходному ответу @ torazaburo стоит отметить, что примеры в этом учебнике не являются утечками. Утечка, что происходит, когда программа удаляет ссылку на что-то, но не освобождает память, которую она потребляет.

Последнее, что я помню, что разработчики JS действительно беспокоились о подлинной утечке, когда Internet Explorer (6 и 7, я думаю) использовал раздельное управление памятью для DOM и JS. Из-за этого удалось связать событие onclick с кнопкой, уничтожить кнопку и по-прежнему иметь обработчик событий, застрявший в памяти - навсегда (или пока браузер не разбился или не был закрыт пользователем). Вы не могли вызвать обработчик или освободить его после факта. Он просто сидел на столе, занимая комнату. Поэтому, если у вас есть долговечный webapp или веб-страница, которая создала и уничтожила множество элементов DOM, вы должны были быть супер-прилежными, чтобы всегда развязывать события, прежде чем уничтожить их.

Я также столкнулся с несколькими неприятными утечками в iOS, но это были все ошибки и были (в конечном итоге) исправлены Apple.

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

function F() { 
    var data = "One megabyte of data"; 

    this.inner = new function() { 
     return data; 
    } 
} 

var G = function() {}; 
G.prototype.data = "One megabyte of data"; 
G.prototype.inner = function() { 
    return this.data; 
}; 

Если вы должны были создать тысячу экземпляров F, браузер должен выделить дополнительный гигабайт оперативной памяти для всех этих копий огромной строки. И каждый раз, когда вы удаляете экземпляр, вы можете получить некоторую ядовитость на экране, когда GC в конечном итоге восстановит этот баран. С другой стороны, если вы сделали тысячу экземпляров G, огромная строка будет создана один раз и повторно использована каждым экземпляром. Это огромный прирост производительности.

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

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

Таким образом, в этом случае существует компромисс между использованием ресурсов и безопасностью.

+0

Хорошо упомянуть добавление общих данных прототипу.Я также отправлю ссылку здесь на [этот вопрос о круговых ссылках в js] (http://stackoverflow.com/q/7347203/2407212), поскольку это также относится к общим убеждениям в отношении утечек памяти. – Jonathan

Смежные вопросы