Проект представляет собой внутреннюю систему мониторинга исключений, разработанную инфраструктурой Vue, которая используется для отображения информации об исключениях при запуске java-программы, включая стек выполнения, код, переменные и другую информацию.
Во время теста коллеги из отдела сообщили, что многократное переключение между различной ненормальной информацией может привести к сбою веб-страницы. Откройте диспетчер задач хрома на месте преступления и увидите, что использование памяти этой страницы достигло 9,7 Гб, изначально подозревая, что на странице есть утечка.
Проверить предположение
Откройте devtool -> производительность, чтобы начать запись производительности страницы.
Выполнение операций, которые переключают другую информацию об исключениях на странице (страница, которая, скорее всего, вызовет утечку памяти).
Просмотр результатов профилирования
Вы можете увидеть пошаговый рост узлов, прослушивателей и головы JS (памяти), а узел роста в середине соответствует каждой операции. Очевидно, что эта операция вызовет непрерывный рост памяти, и логично, что со временем произойдет переполнение памяти.
анализ проблемы
Прежде чем я это сделаю, информация, которую я знаю, такова:
С помощью инструмента производительности вы можете увидеть кумулятивный рост JS Heap, Nodes, Listeners.
Вышеупомянутые три пункта на самом деле имеют зависимости:
Переменные относятся к DOM
Дочерний DOM не может быть выпущен, что приведет к тому, что родитель также не будет выпущен.
Если DOM может быть проверен обычным способом, прослушиватели событий в DOM также будут автоматически удалены.
Моментальные снимки памяти могут быть собраны и проанализированы с помощью devtool-> memory -> make snapshotдокументация по инструменту;
Краткое введение в снэпшот
Ну и содержания немного больше, но все равно понятно:
Статистика по типу данных, вы можете увидеть некоторые встроенные объекты, объекты Vue, пользовательские объекты (такие как Exception, StackFrame и т. д.), Detached Element, EventListener и т. д.
Ордината имеет значение Distance, small size, Retained Size, что может быть неточно понято как:
Distance: опорное расстояние до корня
Shallow size: Размер самого объекта, исключая размер данных, на которые он ссылается.
Retained size: Размер самого объекта и всех ссылок равен общей памяти, занимаемой объектом(Если на объект, на который он ссылается, не ссылаются другие неперерабатываемые объекты. Описание сайта разработчика google называется: размер памяти, освобождаемой после удаления самого объекта вместе со связанными объектами, которые не могут быть доступны из GC корень)
Под панелью фиксаторов вы можете увидеть конкретный ссылочный путь переменной, где она создается и где используется.
Проблема расположения: найти объекты, на которые есть ссылки и которые должны быть освобождены, но на самом деле не выпущены
Выполнить операцию, вызвавшую утечку памяти
Основной код этой операции примерно такой
Основная функция заключается в том, что каждый раз, когда выполняется setEvent, this.exception указывает на новый экземпляр и передается на страницу для отображения данных, а объект, на который ранее ссылался this.exception, должен быть освобожден.
Повторный сбор новой информации о моментальных снимках памяти
Найти отличия: изменить представление на представление различий
Из рисунка видно, что после шага 1 появилось много новых объектов, но удаленных объектов 0.
Взяв в качестве примера объект Exception, в соответствии с логикой кода шага 1 новый объект устанавливается, старый объект освобождается, а дельта должна быть равна нулю. Так что ясно, что здесь есть проблема. Однако щелкните здесь, чтобы просмотреть справочные сведения о переменных, и я не получу много полезной информации.Я знаю только, на какой компонент vue имеется ссылка, но компонентов слишком много, и их нелегко найти.
Глядя на Listeners, стиль, который я вижу, в основном такой:
Как и ожидалось, это было вызвано тем, что некоторые узлы не были выпущены. Однако информации для анализа не так много.
Кроме того, проверьте информацию, относящуюся к узлам, найдите Detached, и вы можете увидеть некоторые Detatched HTMLDivElement и другие подобные объекты, то есть элементы, которые находятся в памяти, но не отображаются на странице.
Я нашел для анализа Detatched HTMLPreElement с относительно небольшой детлой и четкой функцией node (то есть элемент, используемый для подсветки кода на странице):
Вы можете видеть, что фактическая ссылочная связь такова: div
Здесь $platform.event — это API событий, предоставляемый архитектурой платформы + модуля для глобальной связи событий.
Наконец, приведенная выше ссылочная связь переводится: событие $platform.event, предоставляемое платформой (глобальное, связанная функция события не будет автоматически выпущена), событие, называемое var-hover => функция события var-hover, привязана. el свойство vue-component => component ссылается на Dom => родитель Dom ссылается дочерним элементом и не может быть проверен GCed.
Взгляните на код var-hover:
var-hover связывает анонимную функцию (по сути, вы можете знать, что для этого события не было написано никакой операции отвязки), а затем анонимная функция использует this, который является текущим компонентом vue, что также приводит к тому, что этот компонент ссылается на него. Объекты не может быть GCed.
Таким образом, проклятие в основном найдено, следующее, что нужно сделать, это: восстановить -> перепроверить
ремонт
Первая простая модификация: Отвязать событие в beforeDestroy, когда проверка подтвердит, что проблема с переполнением памяти решена
Ручная отвязка — это большая яма, во многих местах у многих людей не обязательно есть эта хорошая привычка при написании кода. У всех есть актуальное решение: преобразовать интерфейс платформы для поддержки автоматической отвязки событий на основе компонентов. код показывает, как показано ниже:
Это фактическая реализация $platform.event
Привязка события var-hover выглядит следующим образом
Удален хук beforeDestroy, и бизнес-уровень тоже выглядит намного лучше.
проверять
Используйте функцию производительности для выполнения операций, которые ранее вызывали многократное переполнение памяти, и получите следующие результаты.
Каждый пик здесь — результат выделения памяти при только что выполненной операции, после каждого выполнения не происходит накопления памяти и Nodes и Listeners.
Опять же, сравните результаты анализа производительности перед исправлением
Ужасные лестницы. . .
Кстати, что изменилось в панели памяти?
Для представления этого объекта StackFrameVar существует дополнительный StackFrameVar и несколько EventListeners, Observers и т. д. Это связано с разницей в данных, представленных дважды, что является нормальным явлением.
Дельта многих объектов, таких как Исключение и т. д., уже равна 0 (в порядке, обратном Дельте).
другие инструкции
Приведенная выше диаграмма анализа представляет собой анализ в реальном времени после перезаписи части кода в процессе написания этой статьи и, условно говоря, не такой подробный, как реальная отладка. Другие операции также были выполнены в процессе фактической отладки:
Окно конфиденциальности, отключите все расширения (чтобы не влиять на анализ памяти)
Отключите функцию HMR в режиме разработки, потому что VUE_HOT_RELOAD также генерирует слой ссылок, я не могу полностью доверять этому.
Используя смоделированные данные, каждый раз, когда выполняется операция, одни и те же данные, которые можно рассчитать вручную (зная, какой класс будет генерировать, сколько экземпляров), будут отображать одни и те же данные.
Ручной сборщик мусора во время выступления
Цель вышеуказанного метода состоит в том, чтобы обеспечить полностью чистую и контролируемую среду анализа.