Saya menemukan bahwa menggunakan AngularFireAuthModule
dari '@angular/fire/auth';
menyebabkan kebocoran memori yang menyebabkan crash browser setelah 20 jam.
Versi: kapan:
Saya menggunakan versi terbaru yang diperbarui hari ini menggunakan ncu -u untuk semua paket.
Api sudut: "@angular/fire": "^5.2.3",
Versi Firebase: "firebase": "^7.5.0"
,
Cara mereproduksi:
Saya membuat kode reproducible minimum pada editor StackBliztz
Berikut ini tautan untuk menguji bug langsung dari tes StackBlizt
Gejala:
Anda dapat memeriksa sendiri bahwa kode tidak melakukan apa-apa. Itu hanya mencetak halo dunia. Namun, memori JavaScript yang digunakan oleh Aplikasi Angular meningkat sebesar 11 kb / s (Chrome Task Manager CRTL + ESC). Setelah 10 jam membiarkan browser terbuka, memori yang digunakan mencapai sekitar 800 mb (jejak memori sekitar dua kali 1,6 Gb !)
Akibatnya, browser kehabisan memori dan tab chrome mogok.
Setelah penyelidikan lebih lanjut menggunakan profil memori chrome di bawah tab kinerja, saya dengan jelas melihat bahwa jumlah pendengar meningkat 2 setiap detik dan dengan demikian tumpukan JS bertambah.
Kode yang menyebabkan kebocoran memori:
Saya menemukan bahwa menggunakan AngularFireAuthModule
modul menyebabkan kebocoran memori apakah itu disuntikkan dalam component
konstruktor atau dalam 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){
}
}
Pertanyaan :
Ini bisa menjadi bug dalam implementasi FirebaseAuth dan saya sudah membuka masalah Github, tetapi saya sedang mencari solusi untuk masalah ini. Saya sangat membutuhkan solusi. Saya tidak keberatan meskipun sesi lintas tab tidak disinkronkan. Saya tidak membutuhkan fitur itu. Saya membaca suatu tempat itu
jika Anda tidak memerlukan fungsi ini, upaya modularisasi Firebase V6 akan memungkinkan Anda untuk beralih ke localStorage yang memiliki peristiwa penyimpanan untuk mendeteksi tab lintas perubahan, dan mungkin akan memberi Anda kemampuan untuk mendefinisikan antarmuka penyimpanan Anda sendiri.
Jika itu satu-satunya solusi, bagaimana cara mengimplementasikannya?
Saya hanya perlu solusi apa pun yang menghentikan peningkatan pendengar yang tidak perlu ini karena memperlambat komputer dan membuat aplikasi saya mogok. Aplikasi saya harus berjalan selama lebih dari 20 jam sehingga sekarang tidak dapat digunakan karena masalah ini. Saya sangat membutuhkan solusi.
Jawaban:
TLDR: Meningkatkan jumlah pendengar adalah perilaku yang diharapkan dan akan diatur ulang saat Pengumpulan Sampah. Bug yang menyebabkan kebocoran memori di Firebase Auth telah diperbaiki di Firebase v7.5.0, lihat # 1121 , periksa Anda
package-lock.json
untuk mengonfirmasi bahwa Anda menggunakan versi yang benar. Jika tidak yakin, instal ulangfirebase
paket.Firebase versi sebelumnya melakukan polling IndexedDB melalui Promise chaining, yang menyebabkan kebocoran memori, lihat JavaScript's Promise Leaks Memory
Diperbaiki pada versi selanjutnya menggunakan panggilan fungsi non-rekursif:
Mengenai jumlah pendengar yang meningkat secara linear:
Diharapkan peningkatan jumlah pendengar yang meningkat karena inilah yang dilakukan Firebase untuk melakukan polling pada IndexedDB. Namun, pendengar akan dihapus kapan pun GC menginginkannya.
Baca Edisi 576302: kebocoran memori (pendengar xhr & muat) salah
Untuk mengonfirmasi bahwa pendengar yang terlepas adalah sampah yang dikumpulkan, saya menambahkan cuplikan ini untuk menekan tumpukan JS, sehingga memaksa GC untuk memicu:
Seperti yang Anda lihat, pendengar yang terpisah dihapus secara berkala ketika GC dipicu.
Pertanyaan stackoverflow serupa dan masalah GitHub mengenai jumlah pendengar dan kebocoran memori:
sumber
this.auth.auth.setPersistence('none')
dingOnInit
bukannya konstruktor kegigihan menonaktifkan.ngOnInit
?setPersistence
dan menemukan bahwa jika hal itu dilakukan dalam konstruktor, pemanggilan fungsi masih dilakukan ke IndexedDB, sedangkan jika itu dilakukan dalamngOnInit
, tidak ada panggilan yang dilakukan ke IndexedDB, tidak persis yakin mengapa