Descobri que o uso AngularFireAuthModule
de '@angular/fire/auth';
causa um vazamento de memória que trava o navegador após 20 horas.
Versão:
Eu uso a versão mais recente atualizada hoje usando ncu -u para todos os pacotes.
Fogo angular: "@angular/fire": "^5.2.3",
Versão Firebase: "firebase": "^7.5.0"
,
Como se reproduzir:
Eu criei um código mínimo reproduzível no editor StackBliztz
Aqui está o link para testar o bug diretamente Teste StackBlizt
Sintoma:
Você pode verificar se o código não faz nada. Apenas imprime olá mundo. No entanto, a memória JavaScript usada pelo aplicativo Angular aumenta em 11 kb / s (Chrome Task Manager CRTL + ESC). Após 10 horas deixando o navegador aberto, a memória usada atinge aproximadamente 800 mb (o espaço de memória é cerca de duas vezes 1,6 Gb !)
Como resultado, o navegador fica sem memória e a guia chrome falha.
Após uma investigação mais aprofundada usando o perfil de memória do chrome na guia desempenho, notei claramente que o número de ouvintes aumenta em 2 a cada segundo e, portanto, o heap JS aumenta de acordo.
Código que causa o vazamento de memória:
Eu descobri que o uso do AngularFireAuthModule
módulo causa vazamento de memória, seja ele injetado em um component
construtor ou em um 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){
}
}
Pergunta :
Pode ser um bug na implementação do FirebaseAuth e já abro um problema no Github, mas estou procurando uma solução alternativa para esse problema. Estou desesperado por uma solução. Eu não me importo, mesmo que as sessões entre guias não estejam sincronizadas. Eu não preciso desse recurso. Eu li em algum lugar que
se você não precisar dessa funcionalidade, os esforços de modularização do Firebase V6 permitirão que você alterne para o localStorage, que possui eventos de armazenamento para detectar alterações nas guias cruzadas, e possivelmente fornecerá a capacidade de definir sua própria interface de armazenamento.
Se essa é a única solução, como implementar isso?
Eu só preciso de qualquer solução que interrompa esse aumento desnecessário de ouvinte, porque diminui a velocidade do computador e trava meu aplicativo. Meu aplicativo precisa ser executado por mais de 20 horas e, portanto, agora não pode ser usado devido a esse problema. Estou desesperado por uma solução.
Respostas:
TLDR: O aumento do número de ouvintes é um comportamento esperado e será redefinido na coleta de lixo. O bug que causa vazamento de memória no Firebase Auth já foi corrigido no Firebase v7.5.0, consulte o nº 1121 , verifique
package-lock.json
se você está usando a versão correta. Se não tiver certeza, reinstale ofirebase
pacote.As versões anteriores do Firebase estavam pesquisando o IndexedDB por meio do encadeamento Promise, o que causa vazamentos de memória, consulte Promise Leaks Memory do JavaScript
Corrigido nas versões subseqüentes usando chamadas de função não recursivas:
Em relação ao aumento linear do número de ouvintes:
Espera-se um aumento linear da contagem de ouvintes, pois é isso que o Firebase está fazendo para pesquisar o IndexedDB. No entanto, os ouvintes serão removidos sempre que o GC desejar.
Leia a edição 576302: Exibição incorreta de vazamento de memória (ouvintes xhr & load)
Para confirmar que os ouvintes desanexados são coletados de lixo, adicionei este snippet para pressionar o heap JS, forçando o GC a disparar:
Como você pode ver, os ouvintes desanexados são removidos periodicamente quando o GC é acionado.
Perguntas similares ao stackoverflow e problemas do GitHub com relação ao número do ouvinte e vazamentos de memória:
fonte
this.auth.auth.setPersistence('none')
emngOnInit
vez do construtor para desativar a persistência.ngOnInit
?setPersistence
e descobri que, se for feito no construtor, ainda serão feitas chamadas de função ao IndexedDB, enquanto que, se for feitongOnInit
, nenhuma chamada será feita ao IndexedDB, não exatamente certo por que embora