Циклическое добавление/удаление узлов DOM вызывает утечку памяти в JavaScript?

Я пытаюсь отображать динамически изменяемые данные, манипулируя элементами DOM (добавляя/удаляя их). Я обнаружил очень странное поведение почти всех браузеров: после того, как я удалил элемент DOM, а затем добавил новый, браузер не освобождает память, занятую удаленным элементом DOM. Посмотрите код ниже, чтобы понять, что я имею в виду. После того, как мы запустим эту страницу, она постепенно съест до 150 МБ памяти. Кто-нибудь может объяснить мне это странное поведение? Или может я что-то не так делаю?

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
  <script type="text/javascript">
     function redrawThings() {
         // Removing all the children from the container
         var cont = document.getElementById("container");
         while ( cont.childNodes.length >= 1 ) {
            cont.removeChild(cont.firstChild);
         }

         // adding 1000 new children to the container
         for (var i = 0; i < 1000; i++) {
             var newDiv = document.createElement('div');
             newDiv.innerHTML = "Preved medved "  + i;
             cont.appendChild(newDiv);             
         }         
     }  
  </script>
  <style type="text/css">
    #container {
        border: 1px solid blue;
    }
  </style>
</head>
<body onload='setInterval("redrawThings()", 200);'>
  <div id="container"> </div>
</body>
</html>

person Igor Tkachenko    schedule 20.09.2010    source источник
comment
Одно замечание: не используйте строку в качестве параметра setInterval. : «Использование этого синтаксиса не рекомендуется по тем же причинам, что и использование оценить()"   -  person Marcel Korpel    schedule 20.09.2010
comment
Спасибо за заметку Марсель! Я не знал такого синтаксиса.   -  person Igor Tkachenko    schedule 20.09.2010


Ответы (3)


Я не могу воспроизвести это на FF 3.6.8/Linux, но 200 мс для таймера — это довольно мало при таком количестве повторного рендеринга DOM. Что я заметил на своей машине, так это то, что при выполнении операций с интенсивным использованием JavaScript, помимо запуска этого скрипта, например, при наборе текста в этом поле для ответов, использование памяти увеличивается, но снова освобождается, когда я перестаю печатать (в моем случае до чего-то около 16% памяти). использование).

Я предполагаю, что в вашем случае у сборщика мусора браузера просто не хватает «свободного времени», чтобы фактически удалить эти узлы из памяти.

person Marcel Korpel    schedule 20.09.2010
comment
Я провел еще несколько исследований и обнаружил, что мои первоначальные выводы были неверны — браузеры не теряют память, они съедают ее до определенного количества, а затем выделенная память падает (и затем снова начинает расти шаг за шагом) . Например. Память Chrome увеличивается до 150 МБ, а затем снижается до 75 МБ (правда, не до 7 МБ изначально!), потребление памяти IE не растет вообще. Странно, но перед тем, как задать вопрос в stackoverflow, я наблюдал устойчивый рост потребления памяти в Chrome, IE и FF, и после того, как все заработало, как предполагалось. Большое спасибо всем, кто спросил! - person Igor Tkachenko; 20.09.2010

Не уверен, повлияет ли это на время, и, вероятно, это действительно плохая практика, но вместо того, чтобы перебирать дочерние узлы, не могли бы вы просто установить innerHTML div на "" ??

person Alex    schedule 20.09.2010

Это связано с тем, что удаление узла из дерева DOM не удаляет его из памяти, он по-прежнему доступен, поэтому следующий код будет работать:

var removed = element.removeChild(element.firstChild);
document.body.appendChild(removed);

Этот код удалит первый дочерний элемент из element, а затем, после его удаления, добавит его в конец документа. На самом деле вы ничего не можете сделать, кроме как сделать свой код более эффективным с меньшим количеством удалений.

Для получения дополнительной информации посетите страницу Node.removeChild в Центре разработчиков Mozilla.

person Randy the Dev    schedule 20.09.2010
comment
Но если он ничему не назначен (как в OP), сборщик мусора удалит его из памяти. Это не причина. - person slebetman; 20.09.2010
comment
Нет, это причина, даже если она не назначена, сборщик мусора ее не удалит. Даже removed = null не удалит ссылку из памяти, единственный способ сделать это — обновить страницу. - person Randy the Dev; 20.09.2010
comment
Да, могу подтвердить: сборщик мусора существует и собирает удаленные из DOM элементы. Но он работает в разных браузерах по-разному, например. В моем случае Chrome начал работать только после того, как достиг около 150 МБ. - person Igor Tkachenko; 20.09.2010