Angular: Память браузера не освобождается при уничтожении компонентов

Созданный на 30 дек. 2016  ·  123Комментарии  ·  Источник: angular/angular

Я отправляю ... (отметьте "x")

[X] bug report => search github for a similar issue or PR before submitting
[ ] feature request
[ ] support request => Please do not submit support request here, instead see https://github.com/angular/angular/blob/master/CONTRIBUTING.md#question

Текущее поведение

При запуске ComponentRef.destroy () память браузера никогда не освобождается. Все внедренные зависимости все еще существуют и работают, даже если компонент ушел.

Также:
При загрузке приложения в iframe уничтожение этого приложения и удаление iframe из DOM не освобождает память браузера.

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

Ожидаемое поведение

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

Минимальное воспроизведение задачи с инструкциями

ДЕЙСТВИЯ ПО ВОСПРОИЗВЕДЕНИЮ

ВОСПРОИЗВЕДЕНИЕ УТЕЧКИ ЗАВИСИМОСТИ
1) Запустите Tour of Heroes plnkr в Chrome или IE
https://plnkr.co/edit/R9axXVtjep5VO8hWygpA?p=preview

2) Откройте консоль js и наблюдайте за логированием в сервисе героя, которое не прекращается после уничтожения приложения.

  • Обратите внимание, это пример Tour of Heroes, за исключением того, что после загрузки приложения в main.ts я добавил таймер на 15 секунд, который запускает ref.destroy (). Кроме того, в файле hero.service.ts я добавил консольный журнал в конструктор, который печатает каждую секунду. Вы можете видеть, что служба продолжает работать даже после запуска .destroy () для компонента ref.

ВОСПРОИЗВЕДЕНИЕ УТЕЧКИ IFRAME

  1. Запустите живой пример Tour of Heroes в IE 11
    https://angular.io/resources/live-examples/toh-6/ts/eplnkr.html
  1. Откройте инструменты разработчика F12, нажмите «Память» и нажмите «Начать профилирование, чтобы начать сеанс производительности».

  2. Внесите изменения в файлы, добавив разрывы строк. Каждый раз, когда приложение перезагружается в iframe, объем памяти увеличивается. Примерно через 15 минут приложение заблокируется из-за того, что на вкладке IE достигнут предел памяти 1,5 ГБ. Если вы протестируете приложение, у которого больше зависимостей, оно заблокируется намного быстрее.

Это можно воспроизвести и в Chrome, просто для того, чтобы утечка памяти увеличилась, требуется больше времени.

Какова мотивация / вариант использования для изменения поведения?

Крупномасштабные приложения angular, которые используют iframe или полагаются на уничтожение компонентов, со временем будут терять память и в конечном итоге давать сбой или блокировку.

Расскажите, пожалуйста, о вашей среде:

Mac OS 10.11.6

  • Угловая версия: 2.0.X

    Угловой v2.4.1
  • Браузер: [все | Хром XX | Firefox XX | IE XX | Safari XX | Мобильный Chrome XX | Веб-браузер Android XX | iOS XX Safari | iOS XX UIWebView | iOS XX WKWebView]

    все
  • Язык: [все | TypeScript XX | ES6 / 7 | ES5]
    все
  • Узел (для проблем AoT): node --version =
core medium bufix

Самый полезный комментарий

Эй, ребята. Это проблема производительности с декабря. Я считаю это критически важным, особенно для средних и крупных приложений, которые имеют несколько представлений / компонентов. Я считаю, что это нужно было решить в версии 5. Есть какие-нибудь комментарии по этому поводу? @mhevery @tbosch

Все 123 Комментарий

Для информации, здесь есть ошибка в IE: https://connect.microsoft.com/IE/Feedback/Details/1742276

У меня была эта проблема на работе.

@ rmullins9, пожалуйста, добавьте

@wKoza - Да, я думаю, что ошибка IE, возможно, играет роль в проблеме iframe. По-прежнему похоже, что angular не очищает инжектор или зависимости от destroy (). @DzmitryShylovich - я скоро что-нибудь

@ rmullins9 можешь попробовать в прод-режиме?

Привет @DzmitryShylovich - извините за задержку. Да, я пробовал в прод-режиме с теми же результатами. Я провел больше исследований по этому поводу и собрал быстрое демонстрационное репо, частично основанное на
https://github.com/rmullins9/angular2-service-destruction

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

Возьмем, к примеру, службу HeroTaxReturnService:
https://angular.io/docs/ts/latest/guide/hierarchical-dependency-injection.html
@Injectable ()
экспортный класс HeroTaxReturnService {
частный currentTaxReturn: HeroTaxReturn;
частный originalTaxReturn: HeroTaxReturn;
конструктор (частный heroService: HeroesService) {}

.. Любая память, которую браузер выделяет для переменных currentTaxReturn и originalTaxReturn, никогда не будет освобождена сборщиком мусора, пока вкладка браузера не будет закрыта.

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

кажется, что есть ссылки на AppModule, хранящиеся в окне

даже с включенным режимом прод? ты 4.0 пробовал?

Я пробовал с enableProdMode. Однако я еще не пробовал версию 4. Я использую angular cli. Есть ли способ использовать версию 4?

вы можете создать новый проект, используя флаг --ng4 или вы можете просто обновить зависимости и версию машинописного текста в package.json

Хорошо. Я попробую это. Его EOD для меня здесь, так что мне придется попробовать его сегодня вечером или утром.

Хорошо. Я пробовал с v 4.0.0 RC 1 и вижу ту же проблему. Я загрузил простое приложение по адресу https://github.com/mcgraphix/angular-destroy-bug
Если вы запустите это приложение с помощью ng serve и нажмете кнопку уничтожения, оно вызовет destroy () как на платформе, возвращенной из platformBrowserDynamic (), так и на destroy () в NgModuleRef, с которым разрешается обещание начальной загрузки. После вызова destroy приложение уходит, а корневой узел приложения удаляется из dom. Однако вызов window.getAllAngularRootElements () возвращает ссылку на него. Итак, я бы предположил, что API-интерфейс Testability вызывает утечку памяти, не выпуская ссылки на него. Не говоря уже о том, что Testability, кажется, добавляет методы в окно. Я не тестировал его, но, судя по коду, похоже, что если вы загрузите два приложения на страницу, второе будет перезаписывать первое. И ни в коем случае никогда не удаляются те методы, что кажется плохим ИМХО.

@mcgraphix да, вы правы. Я отправлю исправление.

вызов window.getAllAngularRootElements () возвращает ссылку на него. Итак, я бы предположил, что API-интерфейс Testability вызывает утечку памяти, не выпуская ссылки на него.

Я исправил это.

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

но это немного сложнее исправить. Необходимо ввести какой-то идентификатор приложения.

Спасибо, что так быстро посмотрели. Есть ли способ через какого-нибудь провайдера полностью отключить регистрацию тестируемости? Я вижу, что есть _NoopTestabilityGetter, который, похоже, не добавляет что-либо в окно. Как можно это использовать? Если бы это можно было сделать, по крайней мере, на данный момент это была бы работа в продакшене.

если enableProdMode не отключает, тогда нет

Это не так. Похоже, в BrowserGetTestability есть статический метод init (), который создает экземпляр BrowserGetTestability и устанавливает его с помощью setTestabilityGetter (). Этот метод инициализации вызывается из метода initDomAdapter () из browser.ts. Я ожидал, что enableProdMode будет правильным местом, чтобы отключить это. Я могу попытаться просто взломать скомпилированный файл JS, чтобы не вызывать этот метод инициализации и посмотреть, решит ли это проблему. Хотя это может не иметь никакого эффекта без вашего исправления для TestabilityRegistry. Простите меня ... Я новичок в подобных вещах, но есть ли способ объединить ваши изменения и перестроить Angular 4 локально, чтобы протестировать ваше исправление, не дожидаясь, пока оно будет объединено по-настоящему?

Думаю, будет проще изменить уже скомпилированный файл js внутри модулей node, если он вам понадобится как можно скорее. это просто 1 LOC

Я могу попробовать это. Это просто вопрос добавления этого:

TestabilityRegistry.prototype.ngOnDestroy = function () {this._applications.clear ();};

Вчера вечером и сегодня утром я немного покопался в этом. Похоже, что ngOnDestroy из TestabilityRegistry не вызывается при вызове destroy () на NgModuleRef или платформе. Так что делаю пару «хаков». Сначала я закомментировал содержимое функции setTestabilityGetter, так что BrowserGetTestability не используется. во-вторых, в addToWindow NoopGetTestability я сохраняю его в окне, чтобы я мог вручную получить к нему доступ для вызова ngOnDestory. Вызов его вручную (а затем удаление ссылки на него в окне) помогает уменьшить использование памяти. После вызова вы можете увидеть, что ссылки на AppModule отсутствуют, когда вы делаете снимок кучи JS. Однако даже после этого вы все равно можете видеть ссылки на ApplicationRef, NgControl и AppView как в window.webpackJsonp, так и в window.assert. Если вы удалите оба из них из окна, снимок кучи больше не будет иметь ссылок на какой-либо из этих объектов, а память уменьшится примерно на 4 мегабайта.

@mcgraphix вы можете добавить plunkr, который воспроизводит проблему?

конечно. Я не уверен, насколько использование памяти Plunkr затуманивает проблему.

Вот plunkr, показывающий проблему, как она есть в 2.x. https://plnkr.co/edit/2O7Tzrt14b6LxRhooeuK?p=preview

Если вы нажмете кнопку Log Info, он выведет на консоль результат вызова getAllAngularRootElements () и getAngularTestability (getAllAngularRootElements () [0]. Нажатие кнопки уничтожения уничтожит как NgModuleRef, так и платформу, как я сказал выше. , нажатие кнопки Log Info по-прежнему вернет ссылки правильно. Если вы сделаете снимок JS Heap Snapshot, вы увидите, что все еще есть ссылки на AppModule, AppModuleInjector, AppView, NgControl, NgZone, NgModuleResolver и т. д. Некоторое из этого можно ожидать, так как вы технически может потребоваться перезагрузить приложение, чтобы все было в порядке. Однако существует много отдельных узлов DOM

Я не знаю, как заставить plunkr использовать v4.

Однако я смог «взломать» решение всех проблем, выполнив несколько вещей в моей локальной сборке. Во-первых, я объединяю все файлы пакета JS в один файл JS и помещаю все это в закрытие. Я добавляю MutationObserver на узел, в котором находится корень приложения, чтобы я мог знать, когда этот узел удален. В моем случае это происходит, когда портал переходит к другому приложению. В обработчике этого наблюдателя я вызываю destroy для NgModuleRef, платформы и маршрутизатора. Затем я удаляю window.assert и window.webpackJsonp. Затем мне нужно вручную вызвать метод ngOnDestroy TestabilityRegistry, потому что он, похоже, не вызывается автоматически. Мне также пришлось взломать какой-то код, чтобы BrowserGetTestability не использовался, а Noop использовался.

При этом я могу восстановить почти всю память приложения. Я уверен, что мой случай не является основным вариантом использования Angular, поэтому я не уверен, насколько это актуально для других, но для нас, в ADP, это проблема или поломка.

Я не знаю, как заставить plunkr использовать v4.

обновить config.js http://plnkr.co/edit/lU32vBJY9KYXZUTW7GQQ?p=preview

@mcgraphix, вы можете попробовать это исправление https://github.com/angular/angular/pull/14819

Эта ошибка не позволяет нам использовать ag-grid с NG4. Пожалуйста, исправьте это как можно скорее! :)

Пока мы ждем, когда это будет объединено - мне было интересно, есть ли какие-нибудь обходные пути без редактирования кода node_modules. Как не использовать что-то?

Привет, может кто-нибудь подтвердить, что это исправление действительно работает для вас?

8 мая 2017 г. в 2:37 утра "benetis-bentley" [email protected] написал:

Пока мы ждем объединения - мне было интересно, есть ли какие-нибудь
обходные пути без редактирования кода node_modules. Как не использовать что-то?

-
Вы получаете это, потому что вас назначили.
Ответьте на это письмо напрямую, просмотрите его на GitHub
https://github.com/angular/angular/issues/13725#issuecomment-299820295 ,
или отключить поток
https://github.com/notifications/unsubscribe-auth/AAqKfypTvbkFofyxb8n5TXl8gN1Mxj5Iks5r3uJ0gaJpZM4LYXyb
.

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

Есть новости по этому поводу? Сегодня в моем собственном коде произошла утечка памяти, которая возникает только при включении enableProdMode (). Оказалось, что с помощью changeDetection: OnPush в одном из наших компонентов удалось решить проблему, хотя это кажется действительно странным. При обнаружении изменений по умолчанию объем памяти быстро увеличился, и приложение перестало отвечать примерно через 1 минуту и ​​использование памяти 1 ГБ (с использованием Chrome). Может быть, это связано с этой ошибкой?

Первоначально я придумал обходной путь (известный как хак), чтобы исправить это в Angular 2.x. Однако в Angular 4 это не работает. Проблема, похоже, в том, что вызов appModule.destroy () не удаляет все из памяти. В частности, я вижу, что подписчики наблюдаемого router.events продолжают получать значения даже после того, как маршрутизатор и appModule были уничтожены. Это ГЛАВНАЯ ПРОБЛЕМА для приложений, которые интегрируются в среду портала. Исходное исправление @DzmitryShylovich не похоже, чтобы оно когда-либо было объединено.

Поскольку наш клиент сообщил, что наша новая версия программного обеспечения часто не позволяет Chrome отвечать, мы обнаружили, что эта ошибка является основной причиной проблемы, с которой столкнулся наш клиент. Чтобы избежать коммерческих споров, у нас нет другого выбора, кроме как прекратить использование Angular2 в качестве нашей новой структуры до того, как эта ошибка будет исправлена. Пожалуйста, решите эту проблему как можно скорее。

Для всех, кто интересуется «решением», вот мой пример проекта, показывающий, как можно вернуть часть памяти, убрав некоторые ссылки в окне: https://github.com/mcgraphix/angular4-memory-hack

По-прежнему проблема, похоже, мы никогда не сможем перейти на Angular 4 ....

@tbosch метка серьезности повысить до severity4

Эта проблема немного переполнена, но меня особенно интересует утечка, вызванная службами, у которых есть свойства, как указано в этой проблеме. Каков статус этого?

Есть какие-нибудь новости по этому поводу? Я тоже думаю, что это дубликат № 17261

Да какое-нибудь обновление? похоже, что это заметно хуже в последней версии angular 4.4.1 (последняя стабильная)

У меня та же проблема, даже только один экземпляр HomeComponent и RestrictedComponent должен существовать одновременно, предыдущие экземпляры хранятся в памяти:
image

Эй, ребята. Это проблема производительности с декабря. Я считаю это критически важным, особенно для средних и крупных приложений, которые имеют несколько представлений / компонентов. Я считаю, что это нужно было решить в версии 5. Есть какие-нибудь комментарии по этому поводу? @mhevery @tbosch

Эй, мы тоже начали сталкиваться с этой проблемой в нашем приложении. Внедрили практики - AOT, отложенная загрузка, отписывание наблюдаемых объектов, очистка свойств служб при уничтожении компонента или модуля. Однако потребление памяти приложением продолжает расти, и через некоторое время страница становится менее отзывчивой.
Надеюсь, исправление скоро будет доступно. Обновление предназначено для Angular версии 4.

Это похоже на заброшенный пул-реквест из-за того, что Дмитрия забанили, вероятно, примерно в то время, когда он его отправил. Я бы хотел, чтобы это устранило эти проблемы.

Это все еще рассматривается? Я использую Angular 4.4.4 и все еще вижу эту проблему. Это решено в 5.0?

@ ugam44 Я использую версию 5.2.0, которая все еще затронута ...

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

Воспроизведение:

1- Создайте приложение Angular
2- "Уничтожить" его
3- Повторить 1-2 (несколько раз)
4- Проверьте память браузера и узлы

@RaedsLab
мне нравится https://stackblitz.com/edit/angular-gitter-pomh4u?file=app%2Fapp.module.ts
Если вы проверите этот профиль, вы заметите, что зеленые - это DOM GC, а синие - это JS GC, здесь все работает нормально.

image


Профиль-20180129T091914.zip

@RaedsLab ... спасибо ... но ты имеешь в виду ...

  • create an Angular app -> открыть приложение Angular в новой вкладке
  • destroy it -> закройте вкладку или просто откройте на этой вкладке все, что не связано с Angular

@RaedsLab ... просто повторная перезагрузка того же приложения ... шаблон тот же, и переработка сборщика мусора работает в течение 5 секунд ... (Chrome 64.0.3282.119, 64-битная версия в Windows 10 FCU) ...
image

@Toxicable Спасибо, я ценю ваши усилия. Я заглянул в ваш stackblitz, похоже, он работает, я протестирую это в локальном приложении.

@ mlc-mlapis Похоже, ваши результаты соответствуют @Toxicable.

К вашему сведению, это были результаты, которые я получал 17 ноября 2017 года. Что-то могло быть исправлено тем временем, или я мог делать что-то не так.

angular app 1

Я заметил, что страницы в конечном итоге перестают отвечать при переходе между страницами в нашем приложении. При переходе от страницы кажется, что старый компонент останется в памяти, а при переходе на эту страницу другой экземпляр компонента будет создан вместе со старым компонентом.

https://stackblitz.com/edit/angular-gitter-ghqv23?file=app%2Fapp.module.ts

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

screen shot 2018-01-30 at 4 08 43 pm

@hkevin ... хм, есть что-то, связанное с самой средой Stackblitz ... не уверен, что это происходит непосредственно из Angular ...

@ mlc-mlapis У меня такая же проблема с моим приложением ...

screen shot 2018-01-30 at 4 50 55 pm

@hkevin Я только что запустил ваш пример, используя полноэкранное окно stackblitz в Chrome. Я видел похожую картину, пока не принудил вручную собирать сборщик мусора в хроме. Вы вручную запускали запуск GC на своем скриншоте?

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

Однако у меня такая же проблема с узлами DOM.
Узлы DOM не удаляются, даже если сборщик мусора запускается вручную (пример stackblitz и реальный мир).

Я вижу то же самое с 5.0.2.

Кажется, что узлы DOM никогда не уничтожаются при создании новых компонентов, даже если они не отображаются на вкладке «Элементы» Chrome. Обработчики событий JS также, похоже, продолжают расти в соответствии с монитором производительности.
На странице, содержащей сетку Kendo с 500 строками, мы увидели, что приложение использует почти 1 ГБ ОЗУ!

При обновлении всей страницы уровни памяти сбрасываются, но затем снова продолжают расти.

У меня та же проблема, что и у @mbrookson. У меня есть приложение Ionic 3.9.2 с угловым 5.0.0. Когда я добавляю карту, количество узлов DOM увеличивается. Когда я удаляю карту, узлы DOM остаются. На изображении ниже показано добавление карты и ее удаление три раза.

domnodes_notcollected

Есть новости по этому поводу? Это оказывает огромное влияние на наш проект, и я ищу способ обхода. Благодарю.

ngOnDestroy не получает звонка, чтобы отменить подписку на мои обращения в службу поддержки. Как убедиться, что ngOnDestroy вызывается, когда я перехожу на другой компонент / страницу. Если нет, то можем ли мы вызвать это вручную?

У меня такая же проблема. Я поддерживаю приложение angular (v5.2.0). Каждые полминуты я обновляю контент, и он должен перерисовывать новые компоненты и удалять старые компоненты.
Я смотрел ngIf, и он работает нормально. (Узлы DOM не существуют в HTML, но остаются отдельными узлами)
performance
Любая идея приветствуется.

@minslay вы смешиваете проекцию контента ( <ng-content> ) и структурные директивы ( ngIf , ngFor ...)?

В своем приложении я вызываю свой API для обслуживания данных.
Я использую * ngIf для рисования своих данных, но если записей не найдено, я рисую шаблон html, который использует

Мой код:

<ng-template ngFor let-item [ngForOf]="_realTimeline " >
  <ng-container [ngSwitch]="item.type">
    <date-bar *ngSwitchCase="'datetime'" [timeStamp]="item.data"></date-bar>
    <commit-bar *ngSwitchCase="'commit'" [commit]="item.data" ></commit-bar>
   ...
  </ng-container>
</ng-template>
<div fxFill *ngIf="_realTimeline.length==0">
    <ng-template #noResults>
        <no-results>
            <h1>No records found</h1>
        </no-results>
    </ng-template>
    <loading-results *ngIf="timelineSrv.loading; else noResults"></loading-results>
</div>

и мойшаблон содержиттег:

<div fxFill fxLayout="row" fxLayoutAlign="center center">
    <img fxFlex="15%" class="logo" src="assets/img/logo.svg" alt="logo">
    <div fxFlex="50%">
        <ng-content></ng-content>
    </div>
</div>

Может быть эта смесь является причиной этой проблемы ??

Благодаря!

Я не совсем понимаю вашу настройку (мы продолжим это в другом месте, как gitter).

Но в основном, если у вас есть компонент A с

<div *ngIf="">
 <ng-content></ng-content>
</div>

используется в родительском компоненте, например

<componentA>
  <h2>Content</h2>
</componentA>

Когда ngIf ложно, содержимое отсоединяется от модели DOM, но не уничтожается, поскольку жизненный цикл содержимого привязан к компоненту, определяющему это содержимое.

Таким образом, в нашем примере <h2> уничтожается только тогда, когда родительский компонент уничтожается.

См. Https://github.com/angular/angular/issues/13059#issuecomment -264598276 и https://github.com/angular/angular/issues/17983.

Я посмотрю на свой код, чтобы проверить, правильно ли я уничтожаю родительский компонент.
Спасибо @ghetolay !!!

Привет, ребята,
@DanielKucal
Я также сталкиваюсь с этой ошибкой, имея много экземпляров в памяти
на ngOnDestroy, добавив приведенный ниже код, память освобождается

Object.keys (this) .map (k => {
это [k] = null;
})

Привет @ghetolay!
Я удалил весь свой код с тегами ng-content, чтобы не было смешанного содержимого (ng-content и ngif), но проблема не исчезла.

@brpradeepprabhu , куда мне добавить этот код? во всех моих функциях OnDestroy? Я новичок в angular ... :(

Спасибо за вашу помощь!

@minslay у меня те же проблемы, с которыми сталкивается

класс экспорта componentA расширяет BaseComponent реализует OnInit, OnDestroy

В базовом компоненте

класс экспорта BaseComponent реализует OnInit, OnDestroy
{
конструктор () {}
ngOnDestroy () {
Object.keys (this) .map (k => {
это [k] = null;
})
}
}

Вышеупомянутое не удалит отсоединенный узел dom. я все еще анализирую, как решить эту проблему

Привет @brpradeepprabhu. Я попробовал ваше решение, но у меня оно не сработало.
Я добавил этот код в конец всех моих функций ngOnDestroy (более 20 компонентов), но у меня те же результаты, что и раньше.

Я продолжу расследование.

Спасибо за уделенное время!

Я наконец решил свою проблему.
Проблема заключалась в том, что мое приложение использует hammerJS, а у меня есть несколько matTooltip. Вы можете увидеть эту ссылку https://github.com/angular/material2/issues/8989, чтобы узнать больше о проблеме.
Моему приложению не нужны утилиты hammerJS, поэтому я закрываю его, и теперь оно работает правильно.

Мы тоже сталкиваемся с этой огромной проблемой. Проблема возникает при загрузке нашего приложения angular в iframe. Объем памяти в браузере продолжает расти, даже если src iframe изменен на другое приложение, отличное от angular.

Что мне нужно было сделать (для моего приложения, предназначенного только для IE), так это перехватить событие DOM onclose и вручную запустить метод деструктора для каждой службы (реализованный с помощью шаблона подписки rxjs). В противном случае эти внедренные зависимости просто не очищались. Однако при этом вся память теперь кажется освобожденной, когда окно закрывается или пользователь перемещается в другое место.
Еще я обернул все методы деструктора (в сервисах и компонентах) с помощью try / catch. Если в деструкторе сгенерирована ошибка javascript, их трудно поймать, и они могут затруднить очистку приложения, поэтому этот дополнительный шаг, казалось, обеспечивал нормальную очистку независимо от любых непредвиденных ошибок.

@anglemd не могли бы вы предоставить пример? Благодарю.

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

В моем приложении, предназначенном только для IE, у меня есть служба (называемая WindowService), единственная задача которой - отслеживать, когда окно браузера закрывается или меняет размер (чтобы я мог сообщить другим компонентам / службам, если они «подписываются» на эти события, предоставляемые службой. ). Сервис вызывает эту строку в конструкторе:
window.addEventListener("unload",this.handleWindowUnload, false );
Эта строка связывает метод частной службы handleWindowUnload () с событием DOM «unload».
Метод handleWindowUnload выполняет две функции: он запускает событие rxjs (см. Ниже), поэтому все объекты-наблюдатели могут обнаружить, что браузер собирается закрыть. Затем он отделяется от DOM следующей строкой:
window.removeEventListener( "unload", this.handleWindowUnload, false );

Таким образом служба узнает о закрытии IE. Теперь он должен поделиться этой новостью с другими службами / классами ....

Сервис предоставляет следующие свойства:
public windowUnload$ = this.windowUnloadSource.asObservable();
private windowUnloadSource = new Subject<void>();

Кроме того, в моем методе handleWindowUnload () я вызываю строку:
this.windowUnloadSource.next( );

Все эти вещи rxjs в основном позволяют другим службам в приложении подписываться на наблюдаемый объект windowUnload $, ждать события выгрузки, а затем они могут вызывать свои собственные функции деструктора. С помощью этого шаблона я мог гарантировать, что деструкторы всегда вызываются. (Чтобы проверить, вызывается ли деструктор, вы можете вызвать alert( "I am being destroyed") из деструктора, и IE отобразит всплывающее окно, которое прерывает фактическое закрытие окна).

Мое приложение работает во встроенном элементе управления InternetExplorer (только IE 10 или IE 11) в другом приложении, поэтому у меня даже нет доступа к стандартным инструментам разработчика браузера. Тем не менее, я могу запустить диспетчер задач Windows, чтобы отслеживать общее использование памяти, и этого было достаточно, чтобы помочь мне определить, когда происходит утечка памяти javascript (что может произойти, если я должен добавить новую службу, которая не полностью очищается после себя) .

Я хочу, чтобы сервисы автоматически очищались за собой в IE с помощью обычных хуков жизненного цикла, но поскольку они этого не делают, я остановился на этом ручном подходе, чтобы гарантировать, что вся память будет освобождена. Это работает достаточно хорошо. Надеюсь, это поможет!

Это критическая проблема, и она все еще не решена .. Angular 👎

@anglemd
можешь попробовать это.

При уничтожении компонента можете ли вы добавить этот фрагмент кода после того, как откажетесь от подписки на свои службы rxjs и убедитесь, что вы реализовали интерфейс OnDeStroy для компонента

ngOnDestroy () {
Object.keys (this) .map (k => {
это [k] = null;
})
}

Да, я видел этот фрагмент кода в обсуждении выше. Разве вы не думаете, что он должен заботиться самим фреймворком?

Да, утечка памяти при включенных всплывающих подсказках (когда всплывающая подсказка показывает какой-то текст) имеет место, не могу понять, с чем связано :(

image

Может быть, как-то связано с узлом AsyncPipe?

image

Любые обновления, так как в моем приложении я вижу непрерывное приращение узлов.

В нашем случае мы загружаем и выгружаем несколько приложений Angular и приложений React. Реагирующие приложения очищают себя должным образом, но при уничтожении модулей приложения angular не уменьшают использование памяти (даже в самом базовом проекте Angular 6).

Есть ли хоть какое-то отношение к этому вопросу? Нам очень сложно стратегически планировать, когда нет никаких указаний на то, будет ли это исправлено, или мы должны искать другое решение.

@ freon27 повезло, что мы уловили это на ранней стадии, перенесли весь наш пользовательский интерфейс на React и с тех пор не оглядывались назад.

Я все еще подписан на эту проблему несколько месяцев спустя, просто чтобы посмотреть, будет ли она когда-либо решена.

После 8 месяцев подъема по кривой обучения Angular и освоения основ работы нашего сайта, мы обнаруживаем серьезные утечки памяти в процессе бета-тестирования. Как же так случилось, что что-то настолько фундаментальное это испорчено?

@ pevans360, скорее всего, ваша проблема в вашем коде, если проблема

Проверьте, если вы

  • Вы отменяете подписку на все наблюдаемые, когда компонент уничтожает
  • Не предоставляйте одну и ту же услугу несколько раз

Первым обычно бывает утечка памяти.

@Itrulia Проблема может быть в их коде. Но я загрузил 2 новых приложения HelloWorld, и эта проблема все еще не устранена.

У меня никогда не было утечек памяти, которые были бы серьезной проблемой (например, этой) из самого Angular.

Поскольку он сказал, что ему не очень нравится Angular, непонимание RxJS может стать проблемой.

@Itrulia , нужно ли мне

const observable = of<number>(1);
const subscription = observable.subscribe(
    (x: number) => console.log(x),
    error => console.error(error),
    () => console.log('completed')
);
subscription.unsubscribe(); // is this necessary?

@ be-ndee, как вы это выразились - в этом нет необходимости. НО, если этот код находится в компоненте, и компонент уничтожается до завершения вашего наблюдаемого - у вас есть утечка.

Итак, это практическое правило: откажитесь от подписки на все, на что вы подписались вручную.

Итак, представим, что у меня есть компонент с этим методом ngOnInit . И компонент уничтожается через 5 секунд (так что setTimeout еще не закончен), тогда я должен отказаться от подписки на наблюдаемый в методе ngOnDestroy ?

ngOnInit(): void {
    const observable = Observable.create(observer => {
        setTimeout(() => {
            observer.next(1);
            observer.complete();
        }, 10000);
    });

    observable.subscribe(() => {});
}

@ be-ndee yep, или вы могли бы использовать шаблон takeUntil:

// in some component
destroyed = new Subject();
ngOnInit(): void {
    this.service.getData()
      .pipe(takeUntil(this.destroyed))
      .subscribe((data) => {
       // do something else
   });
}
ngOnDestroy() {
   this.destroyed.next();
   this.destroyed.complete();
}

РЕДАКТИРОВАТЬ: Я не уверен, как можно было бы очистить тайм-аут в вашем примере, хотя обычно я бы использовал интервал или таймер

Да setTimeout не будет убран.

Вам нужно очистить код компонента только в том случае, если вы подписываетесь вручную и полный обратный вызов не вызывается сразу (например, first() или of() ).

В большинстве случаев вам не нужно вручную подписываться, а использовать асинхронный канал, если вы все-таки подписывались вручную (например, при работе с реактивными формами), вам нужно будет выполнить очистку после себя. Я могу порекомендовать использовать шаблон takeUntil, показанный выше, есть даже пакеты, которые абстрагируют его (но требуют, чтобы у вас был пустой метод ngOnDestroy , что мне не нравится).

@Itrulia Спасибо.

Некоторое время назад мы решили проблему отказа от подписки, используя шаблон takeUntil() .

После вчерашней публикации выяснилось, что по крайней мере часть текущей проблемы связана с HighCharts. После щелчка по сайту в Chrome и создания снимка кучи появляются сотни объектов диаграммы HC (по 50 КБ каждый). Они не исчезают даже после нажатия кнопки «Собрать мусор».

Установка ссылок на объекты диаграммы = null в ngOnDestroy() не помогла, поэтому похоже, что что-то еще мешает сборке мусора. Работаем над некоторыми другими идеями, начиная с нашего компонента оболочки HC.

@ be-ndee Я использую ng2-rx-componentDestroyed для обработки подписок. Работает как шарм.

Реализуйте интерфейс OnDestroy в своем компоненте, а затем вы можете использовать его следующим образом:

Observable.interval(1000)
        .pipe(
            untilComponentDestroyed(this) // <--- magic is here!
        )
        .subscribe(console.log);

См. Https://www.npmjs.com/package/ng2-rx-componentdestroyed

есть ли общепринятый обходной путь
Я вижу некоторые предложения, но не уверен, есть ли у кого-нибудь из Angular какие-либо предложения по этому поводу.
собирался спросить @DzmitryShylovich, но кто-то упомянул, что его

Та же проблема

@brpradeepprabhu Я попробовал ваше исправление, но даже если вы установили все значение null, память не освобождается, поэтому со временем она все еще накапливается.

Я просто хочу сказать, что невозможно поверить, что широко используемый фреймворк, такой как angular, имеет такую ​​ужасную ошибку, которая делает большие приложения бесполезными. Из-за этого мне приходится переписывать все свое мобильное приложение для React Native, и я всегда буду говорить людям держаться подальше от angular.

Мы сталкиваемся с той же NativeScript с нашим настраиваемым RouteReuseStrategy . Мы наблюдаем накопление памяти даже после того, как OnDestroy правильно запущен поверх уничтоженного компонента.
Мы подозреваем, что любые внедренные зависимости все еще существуют и работают даже после того, как компонент исчез.

Есть ли какое-нибудь решение этой проблемы, она через некоторое время сильно сказывается на производительности нашего приложения при работе над приложением?

Я думаю, что эта проблема каким-то образом не попала в поле зрения команды Angular, поскольку они не реагировали на это очень долгое время ... Я бы сказал, постарайтесь привлечь к ней внимание, где вы можете надеяться, что это будет исправлено раньше.

Я попросил предоставить дополнительную информацию об angular и ngrx gitter, и кто-то также открыл ветку по этому поводу на reddit / r / angular2, но пока никаких официальных комментариев:

https://www.reddit.com/r/Angular2/comments/90oc2f/angular_bug_since_2016_memory_isnt_released/

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

Да, это проблема с angular framework, которая не исправлялась годами. Советую переключить проекты на React JS / Native!

Я потратил часы, чтобы изучить это, и это невероятно. Я не сомневаюсь, что React скоро станет основой большинства новых интернет-приложений.

@ 1pocketaces1 ... лол, часы ... похоже, действительно тяжелая работа.

@gabrielalack Я бы порекомендовал выяснить, что не выпущено и почему, это могут быть наблюдаемые объекты, которые не очищаются, или что-то еще, хранящее ссылки. Вкладка памяти в chrome dev tools чрезвычайно полезна и может легко показать вам разницу между снимками кучи и местом хранения ссылок.

@FDIM куча js только увеличивается и никогда не уменьшается. onDestroy () не освобождает память кучи. Даже вышеупомянутое решение обнулить все объекты перед уничтожением не является идеальным решением, поскольку память все еще выделяется.

@ mlc-mlapis это не так уж плохо. Это приложение с 7 страницами на 4 вкладках (не считая онбординговых страниц), бэкэнд aws lambda. На разработку / сборку angular ушло 6 месяцев, но теперь, когда дизайн и макеты настроены, мне понадобится всего пара недель, чтобы переписать в React Native. Я уже вижу большую разницу в производительности при нажатии кнопок и переключении вкладок.

@ 1pocketaces1 ... Это мобильное приложение? ... наконец, оптимизированный для Angular и связанный код немного быстрее, чем просто код React. Но похоже, что вы говорите о другой платформе ... React Native ... так что вам, вероятно, нужно сравнить с Angular -> NativeScript, чтобы получить сопоставимые числа.

Это мобильное приложение. Разница в производительности прикосновений невелика, чтобы быть важной, но она заметна. Этот поток связан с проблемой утечки памяти, поэтому мы оставили angular и не можем его использовать. В проекте с таким количеством страниц с множеством компонентов на каждой странице куча js накапливается, и приложение становится непригодным для использования после навигации по нему.

Я работал над несколькими предприятиями, большими проектами с использованием angular (2-6) с множеством модулей, функций, даже с тяжелыми манипуляциями с домом и т. д.
Я не только никогда не испытывал проблем с производительностью, но и все проекты были невероятно быстрыми и отзывчивыми.

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

@ 1pocketaces1 Наиболее вероятная причина в том, что вы не

@benelliott, мы не использовали никаких наблюдаемых. Я точно знаю, в чем была проблема, lol, я определил ее часами в devTools. Каждый созданный объект навсегда остался в куче памяти.

Я скоро обновлю свое исходное демонстрационное приложение до версии 6, чтобы повторно протестировать эту проблему. Честно говоря, мы давно не видели проблем с утечкой памяти.

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

Тем не менее, в качестве передовой практики мы позаботились о том, чтобы ни одна служба не содержала реквизиты - это плохой способ управлять состоянием приложения и приведет к другим проблемам, которые решает ngrx.

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

Во вторник, 7 августа 2018 г., 13:12 1pocketaces1 [email protected] написал:

@benelliott https://github.com/benelliott мы не использовали
наблюдаемые. Я точно знаю, в чем была проблема, лол, я определил ее с помощью
часов в devTools. Каждый созданный объект остался в динамической памяти
навсегда

-
Вы получили это, потому что оставили комментарий.
Ответьте на это письмо напрямую, просмотрите его на GitHub
https://github.com/angular/angular/issues/13725#issuecomment-411150426 ,
или отключить поток
https://github.com/notifications/unsubscribe-auth/ANNqcFivpavDY3A9rSh5ukGsoEOyAUlGks5uOdh3gaJpZM4LYXyb
.

@ rmullins9 Я бы посоветовал закрыть эту проблему, если у вас больше нет этой проблемы, и позволить другим открывать новую проблему для каждого случая (например, конкретное использование RouteReuseStrategy ). Эта проблема уже выходит за рамки темы, и ее невозможно отследить.

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

В связи с этим возникла проблема, из-за которой метод ngOnDestroy() служб, предоставляемых с помощью useFactory никогда не вызывался. Любая отмена подписки в службе ngOnDestroy, например, $ observableRef.complete (), будет проигнорирована. Это может привести к тому, что компоненты и представления останутся в памяти. Это было исправлено в бета-версии V6.1 https://github.com/angular/angular/commit/fc034270ced8f17cf17a82d3f8382dcef435b9a6

Моя проблема оказалась связана с Google Maps ...
https://issuetracker.google.com/issues/35821412

@benelliott Я читал несколько блогов об объекты, и некоторые блоги, которые я прочитал, говорят, что мне не нужно отказываться от подписки на все наблюдаемые объекты, например http-запросы, потому что они не сохраняют наблюдаемое в памяти после возврата. Это правда ?

@ rafael-andrade Вам не нужно отказываться от подписки на наблюдаемый объект, если вы знаете, что оно завершится до того, как компонент будет уничтожен. Это долгоживущие наблюдаемые, с которыми вам нужно быть осторожным, так как они будут хранить ссылку на подписанный компонент и, таким образом, он не будет уничтожен до тех пор, пока наблюдаемый объект не завершится, что может произойти через достаточно долгое время, когда вы создали тысячи экземпляров. экземпляров этого.

@benelliott Я действительно пытаюсь вникнуть в наблюдаемые, что вы имеете в виду, когда говорите "он завершится". Завершается ли HttpClient.get из angular до того, как компоненты будут уничтожены? Как я могу проверить такие вещи? Например, я подписываюсь на события из модальных окон ngx-bootstrap (https://valor-software.com/ngx-bootstrap/#/modals). Как я могу узнать, завершена ли его наблюдаемая или нет?
Извините, если это глупый вопрос, и спасибо за предыдущий ответ.

Я хотел рассказать, в чем была моя проблема, на случай, если у кого-то будет то же самое. Я использовал ng-lottie для добавления анимации bodymovin в свое приложение. Это было причиной, поскольку пакет lottie не удаляет артефакты анимации в ngOnDestroy, поэтому они заблокированы в памяти кучи.

@briancodes Я читал код ответа HttpClient, это примерно так


        if (ok) {
          // A successful response is delivered on the event stream.
          observer.next(new HttpResponse({
            body,
            headers,
            status,
            statusText,
            url: url || undefined,
          }));
          // The full body has been received and delivered, no further events
          // are possible. This request is complete.
          observer.complete();
        } else {
          // An unsuccessful request is delivered on the error channel.
          observer.error(new HttpErrorResponse({
            // The error in this case is the response body (error from the server).
            error: body,
            headers,
            status,
            statusText,
            url: url || undefined,
          }));
        }

Означает ли это, что при возникновении ошибки наблюдаемое не завершается или завершается где-то еще?

Пожалуйста, отключите это обсуждение.

В среду, 5 сентября 2018 г., 8:23 Rafael Andrade [email protected]
написал:

@briancodes https://github.com/briancodes Я читал HttpClient
ответ, что-то вроде этого

    if (ok) {
      // A successful response is delivered on the event stream.
      observer.next(new HttpResponse({
        body,
        headers,
        status,
        statusText,
        url: url || undefined,
      }));
      // The full body has been received and delivered, no further events
      // are possible. This request is complete.
      observer.complete();
    } else {
      // An unsuccessful request is delivered on the error channel.
      observer.error(new HttpErrorResponse({
        // The error in this case is the response body (error from the server).
        error: body,
        headers,
        status,
        statusText,
        url: url || undefined,
      }));
    }

Означает ли это, что при возникновении ошибки наблюдаемое не завершается?

-
Вы получаете это, потому что подписаны на эту ветку.
Ответьте на это письмо напрямую, просмотрите его на GitHub
https://github.com/angular/angular/issues/13725#issuecomment-418710233 ,
или отключить поток
https://github.com/notifications/unsubscribe-auth/ABq8aqYt3_4Rh2M6kUxxi8s0bpwfeq4Cks5uX8JPgaJpZM4LYXyb
.

@ rafael-andrade Если наблюдаемая ошибка завершилась, она не будет выдавать больше значений, аналогично тому, как это происходит при завершении. Это технически разные события, но да, событие ошибки также закрывает поток.

Вы можете прочитать больше здесь:
http://reactivex.io/rxjs/manual/overview.html#executing -observables

@ rafael-andrade
Проверьте мой ответ на SO: https://stackoverflow.com/a/51732897/3100587?stw=2

У меня была эта проблема с использованием одного спа
Я нашел глупое решение проблемы утечки памяти.
попробуйте поставить этот код до загрузки угловых модулей, может это вам поможет

import { TestabilityRegistry } from '@angular/core';
...
TestabilityRegistry.prototype.registerApplication = () => void 0;
TestabilityRegistry.prototype.unregisterApplication = () => void 0;
TestabilityRegistry.prototype.unregisterAllApplications = () => void 0;
TestabilityRegistry.prototype.getTestability = () => null;
TestabilityRegistry.prototype.getAllTestabilities = () => [];
TestabilityRegistry.prototype.getAllRootElements = () => [];
TestabilityRegistry.prototype.findTestabilityInTree = () => null;

он просто мешает TestabilityRegistry что-либо делать, поэтому он не будет сохранять ссылку на модуль после размонтирования

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

Узнайте больше о нашей политике автоматической блокировки разговоров .

_Это действие было выполнено автоматически ботом. _

Была ли эта страница полезной?
0 / 5 - 0 рейтинги