Я обнаружил, что использование AngularFireAuthModule
from '@angular/fire/auth';
вызывает утечку памяти, которая приводит к сбою браузера через 20 часов.
Версия:
Я использую последнюю версию, обновленную сегодня, используя ncu -u для всех пакетов.
Угловой огонь: "@angular/fire": "^5.2.3",
Firebase версия: "firebase": "^7.5.0"
,
Как воспроизвести:
Я сделал минимально воспроизводимый код в редакторе StackBliztz
Вот ссылка для тестирования ошибки непосредственно в тесте StackBlizt
Симптом:
Вы можете проверить, что код ничего не делает. Это просто печатает привет мир. Однако объем памяти JavaScript, используемой приложением Angular, увеличивается на 11 кбит / с (Chrome Task Manager CRTL + ESC). Через 10 часов, оставив браузер открытым, объем используемой памяти достигает примерно 800 МБ (объем памяти составляет около 1,6 ГБ !)
В результате браузеру не хватает памяти, а вкладка Chrome вылетает.
После дальнейших исследований с использованием профилирования памяти chrome на вкладке производительности я четко заметил, что число слушателей увеличивается на 2 каждую секунду, и, соответственно, увеличивается куча JS.
Код, который вызывает утечку памяти:
Я обнаружил, что использование AngularFireAuthModule
модуля вызывает утечку памяти, независимо от того, вводится ли он в component
конструктор или в service
.
import { Component } from '@angular/core';
import {AngularFireAuth} from '@angular/fire/auth';
import {AngularFirestore} from '@angular/fire/firestore';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'memoryleak';
constructor(public auth: AngularFireAuth){
}
}
Вопрос :
Это может быть ошибкой в реализации FirebaseAuth, и я уже открыл проблему Github, но я ищу обходной путь для этой проблемы. Я отчаянно нуждаюсь в решении. Я не против, даже если сеансы через вкладки не синхронизированы. Мне не нужна эта функция. Я где-то читал, что
если вам не требуется эта функциональность, усилия по модульности Firebase V6 позволят вам переключиться на localStorage, в котором есть события хранения для обнаружения изменений в кросс-таблицах, и, возможно, предоставят вам возможность определить собственный интерфейс хранения.
Если это единственное решение, как это реализовать?
Мне просто нужно любое решение, которое останавливает это ненужное увеличение числа слушателей, потому что оно замедляет работу компьютера и приводит к сбою моего приложения. Мое приложение должно работать более 20 часов, поэтому из-за этой проблемы его нельзя использовать. Я отчаянно нуждаюсь в решении.
Ответы:
TLDR: увеличение числа слушателей является ожидаемым поведением и будет сброшено при сборке мусора. Ошибка, которая вызывает утечки памяти в Firebase Auth, уже была исправлена в Firebase v7.5.0, см. # 1121 , проверьте,
package-lock.json
чтобы убедиться, что вы используете правильную версию. Если вы не уверены, переустановитеfirebase
пакет.Предыдущие версии Firebase опрашивали IndexedDB через цепочку Promise, что приводит к утечкам памяти, см. JavaScript в Promise Leaks Memory
Исправлено в последующих версиях с использованием нерекурсивных вызовов функций:
Относительно линейно возрастающего числа слушателей:
Ожидается линейное увеличение числа слушателей, поскольку именно это Firebase делает для опроса IndexedDB. Тем не менее, слушатели будут удалены, когда GC захочет.
Прочтите выпуск 576302: неправильно отображается утечка памяти (слушатели xhr и загрузка)
Чтобы подтвердить, что отсоединенные слушатели являются сборщиком мусора, я добавил этот фрагмент для давления на кучу JS, заставляя GC запускать:
Как вы можете видеть, отключенные слушатели периодически удаляются при запуске GC.
Схожие вопросы по стеку и GitHub, касающиеся количества слушателей и утечек памяти:
источник
this.auth.auth.setPersistence('none')
вngOnInit
вместо конструктора , чтобы отключить настойчивость.ngOnInit
?setPersistence
и обнаружил, что если это делается в конструкторе, то вызовы функций по-прежнему выполняются в IndexedDB, тогда как, если это происходитngOnInit
, в IndexedDB не было никаких вызовов, не совсем конечно, почему