2014-01-20 5 views
20

У меня возникли проблемы с диагностикой утечки памяти отдельного DOM-дерева в очень большом одностраничном веб-приложении, созданном в основном с помощью Knockout.Поиск утечки памяти дерева DOM

Я настроил приложение, чтобы прикрепить объект dummy FooBar к определенному элементу кнопки HTML, который должен быть собран с помощью мусора, когда пользователь переходит на другую «страницу» приложения. С помощью моментального снимка в куче, я вижу, что старый (FooBar) экземпляр (который должен был быть GC'ed) по-прежнему доступен из его HTMLButtonElement в (большом) отдельном дереве DOM.

Отслеживание ссылок через удерживающее дерево панель, я следую за цепью, уменьшая расстояние от корня GC. Однако в какой-то момент мой поиск достигает тупика на расстоянии 4 узла от корня (в данном случае)! Сохраняющее дерево вообще не сообщает об этом узле, но как-то знает, что оно находится в четырех шагах от корня GC.

Вот часть подпорного дерева, которое имеет меня озадачил (цифры справа являются расстоянием от корня):

v foobar in HTMLButtonElement         10 
    v [4928] in Detached DOM tree/5643 entries     9 
    v native in HTMLOptionElement        8 
     v [0] in Array           7 
     v mappedNodes           6 
      v [870] in Array          5 
      v itemsToProcess in system/Context    4 
       context in function itemMovedOrRetained() 
       context in function callCallback() 

удерживающей дерево не показывает ссылку здесь на расстоянии 3 или выше.

Может кто-нибудь объяснить это мне? Я надеялся, что смогу следить за ссылочной цепочкой, чтобы вернуться к нарушающей части кода приложения JavaScript, но у меня все в порядке!

+0

Можете ли вы поделиться некоторыми из кода, в частности, где этот элемент создан и где он расположен? – Etai

+0

Я боюсь, что код проприетарный и огромный; У меня не было возможности попробовать воспроизвести его в маленьком. Реальная проблема здесь заключается в том, почему профилировщик Chrome heap должен когда-либо сообщать о чем-то подобном выше, который не выходит за пределы корня (как это может быть ?!). – Rafe

+1

Вы должны удалить свойство 'foobar' (с помощью' delete'; 'delete button.foobar' перед удалением элемента' DOM' и убедитесь, что нет оставшихся прослушивателей событий. – GuyT

ответ

11

Прежде всего - не используйте delete как один из предложенных комментариев. Установка ссылки на null - это правильный способ избавиться от вещей. delete разбивает «скрытый класс». Чтобы убедиться в этом, запустите мои примеры из https://github.com/naugtur/js-memory-demo

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

Я могу помочь вам точно определить проблему.

Сначала перейдите на вкладку Timeline в devtools и используйте ее, чтобы наблюдать, как происходит утечка. Выберите только распределение памяти и начните запись. Пройдите сценарий, который вы ожидаете утечки. Балки, которые остаются синими, являются утечками. Вы можете выбрать их окружение на временной шкале и сосредоточиться на их сохраняющем дереве. Самые интересные элементы в отдельно стоящих домиках - красные - на них ссылаются снаружи. Остальное сохраняется, потому что любой элемент в дереве ссылается, он ссылается на все остальное (x.parentNode)

Если вам нужна дополнительная информация, вы можете сделать несколько снимков в профилировщике, чтобы у вас был моментальный снимок до и после причина утечки (которую вы обнаружили на временной шкале - теперь вы знаете точное действие, которое ее вызывает). Затем вы можете сравнить их в профилировщике - есть «сравнение». что более понятно, чем другие.

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


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

http://naugtur.pl/pres/mem.html#/5/2

но реальное полное руководство по использованию профилировщика памяти этот документ:

https://developer.chrome.com/devtools/docs/javascript-memory-profiling#looking_up_color_coding

Обновлена ​​ссылка:https://developers.google.com/web/tools/profile-performance/memory-problems/memory-diagnosis

+0

Большое спасибо за это! На данный момент мы довольно заняты, но я попробую ваши предложения, как только у меня будет запасной день. Рад видеть, что кто-то соглашается со мной, что след выглядит очень странно. – Rafe

+0

Узлы, выделенные желтым, имеют прямые ссылки на них из кода JavaScript. Узлы, выделенные красным, не имеют прямых ссылок. Они живы только потому, что они являются частью дерева желтого узла. В общем, вы хотите сосредоточиться на желтых узлах. Исправьте свой код, чтобы желтый узел не был жив дольше, чем он должен быть, и вы также избавляетесь от красных узлов, которые являются частью дерева желтого узла. – Legends

+3

Но что, если у вас только красные узлы? – Legends

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