Утечки памяти в замыканиях JavaScript

http://atkinson.posterous.com/javascript-closure-memory-leak
  • Перевод
Цитата из Google JavaScript style guide:

Возможность создавать замыкания — похоже, самая полезная и часто остающаяся без внимания особенность JS.

Однако, одну вещь нужно иметь виду: замыкание хранит указатель на замыкаемый им контекст. В результате, прикрепление замыкания к элементу DOM может породить циклическую зависимость и, следовательно, утечку памяти. Например, в следующем куске кода:

function foo(element, a, b) {
  element.onclick = function() { /* использует a и b */ };
}


замыкание хранит указатель на element, a и b даже в том случае, если оно никогда element не использует. А раз element тоже хранит указатель на замыкание, то получается цикл, который никогда не будет вычищен сборщиком мусора.

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

function foo(element, a, b) {
  element.onclick = bar(a, b);
}

function bar(a, b) {
  return function() { /* использует a и b */ }
}


Похоже, на текущий момент это наиболее часто встречающийся на практике пример утечки памяти в JavaScript.
Метки:
Поделиться публикацией
Похожие публикации
Комментарии 31
  • –6
    спасибо, кеп(это я автору, а не переводчику).

    Вопрос в том как научить программистов тому что javascript это не jQuery, как сказать что это достаточно интересный и своеобразный язык и в нём надо разораться а не применять вслепую.
    • –8
      Я с вам согласен, но к сожалению JavaScript приобрел такую популярность только после jQuery (или подобных), наверное. Еще 3 года назад многие с опасной рассматривали вариант внедрения js — так как не кроссбраузерно и т.д.
      • +4
        Я боюсь после jQuery javascript не получил популярности. Равно как после RoR не стала сильно популярнее первая R.
        • +11
          приобрел популярность? js не кроссбраузерно? есть альтернативы? опасность внедрения js? я в параллельной вселенной? o_O
          • –5
            аватар смените, косяки разметки напрягают на подсознательном уровне
            • +2
              как часто вы собираетесь пялиться на мой аватар?
              • 0
                До вашего аватара мне нет дела, просто использовать в качестве его такую картинку — не очень правильно. Если вы так не считаете, ваше право. Я просто указал на ошибку.
            • 0
              да js ну просто очень опасен
              (function(){arguments.callee();})();
              

              А если серьезно, то привыкая к (а часто начиная с) jQuery, программисты забывают о всей красоте, изящности и возможностях javascript.

              >>Вопрос в том как научить программистов тому что javascript это не jQuery, как сказать что это достаточно интересный и своеобразный язык и в нём надо разораться а не применять вслепую.

              Тут скорее надо заинтересовать, люди часто ленятся взять хорошую книгу по языку и прочитать от корки до корки, гораздо проще ведь нагуглить что-то вроде «увеличение картинки плагин jQuery».
              • 0
                >> привыкая к (а часто начиная с) jQuery, программисты забывают о всей красоте, изящности и возможностях javascript.

                С чего вы взяли что забывают, скорее вообще не знали :)
                • 0
                  А по-моему все вокруг зазря сгущают краски вокруг JQuery. Я начал изучение JS именно с JQ. И лишь спустя время мне стало интересно, а что же у этого JQ внутри, раз он из такого жуткого (как мне тогда казалось) JS делает такую конфетку.
                  Начинать надо с простого. А JQ это просто и наглядно.
              • +1
                Вы не ошиблись ноликом? Я Уже 6 лет на JS пишу и альтернативы до сих пор никакой нет.
              • +3
                Учатся, как практика показывает, на ошибках. А ошибки обычно возникают с первым более-менее содержательным асинхронным вызовом.
                • +6
                  Пост написан автором больше года назад, и там же, в комментах ему на пальцах объяснили, что это уже неактуально.
                  • +6
                    Этому переводу не хватает перевода комментариев из оригинала :)
                  • 0
                    В jQuery, кстати, тоже возможны утечки, если, например, будет смешанное использование jQuery и стандартных вызовов (из-за использования jQuery.cache).
                    • НЛО прилетело и опубликовало эту надпись здесь
                      • +2
                        Ну, утечки памяти — это тоже не совсем язык, а особенности конкретной реализации. И DOM-вызовы — API браузера, то есть тоже своего рода библиотека.
                        • НЛО прилетело и опубликовало эту надпись здесь
                    • +6
                      Это что же за сборщик мусора такой, который не умеет с циклическими зависимостями справляться? А что делать, если нужно использовать в обработчике element, а не только a и b?
                      • –4
                        В php со сборкой мусора с циклическими зависимостями тоже бывает беда.
                      • 0
                        Тут немного коряво, вообще говоря в as3 который тоже EcmaScript 262 циклические ссылки удаляются, но вот если хоть один элемент изх цикла висит на экране весь цикл и повиснет на одном элементе. Это очень частая причина утечек.
                        • +2
                          Если мне не изменяет память, в ECMAScript вообще не определен алгоритм работы сборщика мусора, потому все зависит от конечной реализации.

                          А что касается IE, то до восьмой версии сборщик хоть и работал по принципу mark&sweep, а значит, был способен чистить циклические ссылки, но из-за особенностей работы COM (в котором используется подсчет ссылок) наличие ссылок в expando DOM-элемента на объекты скриптового движка в одном замыкании с объектами, ссылающимися на этот DOM-элемент приводило к таким неразрешимым случаям.
                        • 0
                          Насколько я помню, несвежие IE этим точно страдали
                        • НЛО прилетело и опубликовало эту надпись здесь
                          • –4
                            JS отжирает памяти на большинстве приложений очень нехило, а если учесть что страница может быть открыта продолжительное время, то такого рода статьи грех называть бредом!
                          • +16
                            Фигня. И хром, и фф, и опера, и эксплорер версий 8+ нормально с такими утечками справляются.
                            Ну и для того, чтоб избавиться от них, не надо выносить обработчик наружу, можно в замыкании ссылку на элемент прибить:
                            function foo(element, a, b) {
                              element.onclick = function() { /* использует a и b */ };
                              element = null;
                            }
                            
                            • +1
                              Это актуально только в части браузеров, а точнее — в старых FF и IE.

                              Такая утечка возникает только тогда, когда GC использует «счетчик ссылок» (в FF счетчик ссылок использовался для элементов DOM) и из-за этого не может разрешить циклические ссылки.
                              • НЛО прилетело и опубликовало эту надпись здесь
                                • +1
                                  бред
                                  • 0
                                    Браузеры ведут себя по-разному в таких ситуациях, если открыть такой код через web inspector в хроме, то можно увидеть, что замыкание может и не сохранять те элементы, которые оно не использует, т.е. если внутри onclick колбэка не будет обращения к element, то замыкание не сохранит ссылку на него в разделе Scope variables это можно увидеть

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