Descubrí que el uso de AngularFireAuthModule
from '@angular/fire/auth';
causa una pérdida de memoria que bloquea el navegador después de 20 horas.
Versión:
Utilizo la última versión actualizada hoy usando ncu -u para todos los paquetes.
Fuego angular: "@angular/fire": "^5.2.3",
Versión base de fuego: "firebase": "^7.5.0"
,
Cómo reproducir:
Hice un código mínimo reproducible en el editor StackBliztz
Aquí está el enlace para probar el error directamente Prueba StackBlizt
Síntoma:
Puede comprobar usted mismo que el código no hace nada. Simplemente imprime hola mundo. Sin embargo, la memoria de JavaScript utilizada por la aplicación Angular aumenta en 11 kb / s (Chrome Task Manager CRTL + ESC). Después de 10 horas dejando el navegador abierto, la memoria utilizada alcanza aproximadamente 800 mb (¡la huella de la memoria es aproximadamente el doble de 1,6 Gb !)
Como resultado, el navegador se queda sin memoria y la pestaña de Chrome se bloquea.
Después de una mayor investigación utilizando el perfil de memoria de Chrome en la pestaña de rendimiento, noté claramente que el número de oyentes aumenta en 2 por segundo y, por lo tanto, el montón JS aumenta en consecuencia.
Código que causa la pérdida de memoria:
Descubrí que el uso del AngularFireAuthModule
módulo causa la pérdida de memoria si se inyecta en un component
constructor o en un 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){
}
}
Pregunta :
Podría ser un error en la implementación de FirebaseAuth y ya abro un problema de Github, pero estoy buscando una solución para este problema. Estoy desesperado por una solución. No me importa incluso si las sesiones en las pestañas no están sincronizadas. No necesito esa característica. Leí en alguna parte que
Si no necesita esta funcionalidad, los esfuerzos de modularización de Firebase V6 le permitirán cambiar a localStorage, que tiene eventos de almacenamiento para detectar cambios en las pestañas, y posiblemente le brinde la capacidad de definir su propia interfaz de almacenamiento.
Si esa es la única solución, ¿cómo implementar eso?
Solo necesito cualquier solución que detenga este aumento innecesario de oyentes porque ralentiza la computadora y bloquea mi aplicación. Mi aplicación debe funcionar durante más de 20 horas, por lo que ahora no se puede usar debido a este problema. Estoy desesperado por una solución.
Respuestas:
TLDR: aumentar el número de oyentes es un comportamiento esperado y se restablecerá en la recolección de basura. El error que causa pérdidas de memoria en Firebase Auth ya se ha solucionado en Firebase v7.5.0, consulte # 1121 , verifique
package-lock.json
que confirme que está utilizando la versión correcta. Si no está seguro, reinstale elfirebase
paquete.Las versiones anteriores de Firebase sondeaban IndexedDB a través del encadenamiento de Promise, lo que causa pérdidas de memoria, consulte la memoria de Promise Leaks de JavaScript
Solucionado en versiones posteriores que utilizan llamadas a funciones no recursivas:
En cuanto al número de oyentes que aumenta linealmente:
Se espera un recuento de oyentes en aumento lineal, ya que esto es lo que Firebase está haciendo para sondear IndexedDB. Sin embargo, los oyentes serán eliminados cuando el GC lo desee.
Lea el problema 576302: muestra incorrectamente la pérdida de memoria (oyentes xhr y carga)
Para confirmar que los oyentes separados son basura recolectada, agregué este fragmento para presionar el montón JS, forzando así a GC a disparar:
Como puede ver, los oyentes desconectados se eliminan periódicamente cuando se activa GC.
Preguntas similares de stackoverflow y problemas de GitHub con respecto al número de escucha y pérdidas de memoria:
fuente
this.auth.auth.setPersistence('none')
enngOnInit
lugar del constructor para deshabilitar la persistencia.ngOnInit
?setPersistence
y descubrí que si se realiza en el constructor, las llamadas de función aún se realizan a IndexedDB, mientras que si se realiza enngOnInit
, no se realizaron llamadas a IndexedDB, no exactamente seguro por qué sin embargo