Açısal Firebase uygulaması +1 gigabayt bellek ayırma ile 20 saat sonra çöküyor


13

AngularFireAuthModuleFrom kullanmanın '@angular/fire/auth';tarayıcıyı 20 saat sonra çökerten bir bellek sızıntısına neden olduğunu buldum .

Sürüm:

Tüm paketler için ncu -u kullanarak bugün güncellenen en son sürümü kullanıyorum.

Açısal Ateş: "@angular/fire": "^5.2.3",

Firebase versiyon: "firebase": "^7.5.0",

Nasıl çoğaltılır:

StackBliztz editöründe minimum tekrarlanabilir kod yaptım

İşte doğrudan hata test etmek için bağlantı StackBlizt testi

Belirti:

Kodun hiçbir şey yapmadığını kendiniz kontrol edebilirsiniz. Sadece merhaba dünyayı basar. Ancak, Açısal Uygulama tarafından kullanılan JavaScript belleği 11 kb / s (Chrome Görev Yöneticisi CRTL + ESC) artar . Tarayıcıyı açık bıraktıktan 10 saat sonra, kullanılan bellek yaklaşık 800 mb'ye ulaşır (bellek alanı yaklaşık 1,6 Gb'dir !)

Sonuç olarak, tarayıcıda bellek kalmaz ve krom sekmesi kilitlenir.

Performans sekmesi altında kromun bellek profilini kullanarak daha fazla araştırma yaptıktan sonra, dinleyicilerin sayısının saniyede 2 oranında arttığını ve JS yığınının buna göre arttığını açıkça fark ettim.

resim açıklamasını buraya girin

Bellek sızıntısına neden olan kod:

AngularFireAuthModule Modülün kullanılmasının, ister bir kurucuya, ister componenta 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){

  }
}

Soru :

FirebaseAuth uygulamasında bir hata olabilir ve zaten bir Github sorunu açıyorum, ancak bu sorun için bir geçici çözüm arıyorum . Bir çözüm için umutsuzum. Sekmelerdeki oturumlar senkronize olmasa bile umrumda değil. Bu özelliğe ihtiyacım yok. Bir yerde okudum ki

bu işlevselliğe ihtiyacınız yoksa, Firebase V6 modülerleştirme çabaları, sekmelerdeki değişiklikleri algılamak için depolama olayları olan localStorage'a geçmenize ve muhtemelen kendi depolama arayüzünüzü tanımlamanıza olanak tanır.

Tek çözüm buysa, bunu nasıl uygulayabilirim?

Bilgisayarı yavaşlatıp uygulamamı çöktüğü için dinleyicinin bu gereksiz artışını durduran herhangi bir çözüme ihtiyacım var. Uygulamamın 20 saatten fazla çalışması gerekiyor, bu yüzden şimdi bu sorun nedeniyle kullanılamıyor. Bir çözüm için umutsuzum.



Sorununuzu örneğinizde yeniden oluşturamadım
Sergey Mell

@SergeyMell StackBlitz'e gönderdiğim kodu kullandınız mı?
TSR

Evet. Aslında, bundan bahsediyorum.
Sergey Mell

Kodu indirmeyi ve yerel olarak çalıştırmayı deneyin. Ayrıca, drive.google.com/file/d/1fvo8eJrbYpZWfSXM5h_bw5jh5tuoWAB2/…
TSR

Yanıtlar:


7

TLDR: Dinleyici sayısının artırılması beklenen bir davranıştır ve Çöp Toplama ile sıfırlanır. Firebase Auth'da bellek sızıntılarına neden olan hata, Firebase v7.5.0'da zaten düzeltildi, bkz. # 1121 , package-lock.jsondoğru sürümü kullandığınızı doğrulamak için kontrol edin . Emin değilseniz firebasepaketi yeniden yükleyin .

Firebase'in önceki sürümleri IndexedDB'yi Promise zincirleme yoluyla yok ediyordu, bu da bellek sızıntılarına neden oluyor, bkz. JavaScript Promise Leaks Memory

var repeat = function() {
  self.poll_ =
      goog.Timer.promise(fireauth.storage.IndexedDB.POLLING_DELAY_)
      .then(goog.bind(self.sync_, self))
      .then(function(keys) {
        // If keys modified, call listeners.
        if (keys.length > 0) {
          goog.array.forEach(
              self.storageListeners_,
              function(listener) {
                listener(keys);
              });
        }
      })
      .then(repeat)
      .thenCatch(function(error) {
        // Do not repeat if cancelled externally.
        if (error.message != fireauth.storage.IndexedDB.STOP_ERROR_) {
          repeat();
        }
      });
  return self.poll_;
};
repeat();

Sonraki sürümlerde, özyinelemesiz işlev çağrıları kullanılarak düzeltildi:

var repeat = function() {
  self.pollTimerId_ = setTimeout(
      function() {
        self.poll_ = self.sync_()
            .then(function(keys) {
              // If keys modified, call listeners.
              if (keys.length > 0) {
                goog.array.forEach(
                    self.storageListeners_,
                    function(listener) {
                      listener(keys);
                    });
              }
            })
            .then(function() {
              repeat();
            })
            .thenCatch(function(error) {
              if (error.message != fireauth.storage.IndexedDB.STOP_ERROR_) {
                repeat();
              }
            });
      },
      fireauth.storage.IndexedDB.POLLING_DELAY_);
};
repeat();


Doğrusal olarak artan dinleyici sayısı ile ilgili:

Firebase'in IndexedDB'yi yoklamak için yaptığı şey budur. Ancak, GC istediği zaman dinleyiciler kaldırılacaktır.

Okuma Sayısı 576302: Yanlış bellek (dinleyiciler xhr ve yük) sızıntısı gösteriliyor

V8 periyodik olarak Küçük GC gerçekleştirir, bu da yığın boyutundaki küçük damlalara neden olur. Bunları aslında alev tablosunda görebilirsiniz. Bununla birlikte, küçük GC'ler dinleyiciler için açıkça görülen tüm çöpleri toplamayabilir.

Araç çubuğu düğmesi, dinleyici toplayabilen Major GC'yi çağırır.

DevTools, çalışan uygulamaya müdahale etmemeye çalışır, bu nedenle GC'yi kendi başına zorlamaz.


Ayrılmış dinleyicilerin çöp toplandığını doğrulamak için bu parçacığı JS yığınına baskı uygulamak ve böylece GC'yi tetiklemeye zorlamak için ekledim:

var x = ''
setInterval(function () {
  for (var i = 0; i < 10000; i++) {
    x += 'x'
  }
}, 1000)

Dinleyiciler çöp toplanır

Gördüğünüz gibi, GC tetiklendiğinde ayrı dinleyiciler periyodik olarak kaldırılır.



Dinleyici numarası ve bellek sızıntılarıyla ilgili benzer yığın akışı soruları ve GitHub sorunları:

  1. Chrome geliştirici araçlarının performans profili oluşturma sonuçlarındaki dinleyiciler
  2. JavaScript dinleyicileri artmaya devam ediyor
  3. Bellek sızıntısına neden olan basit bir uygulama?
  4. $ http 'GET' bellek sızıntısı (NOT!) - dinleyici sayısı (AngularJS v.1.4.7 / 8)

7.5.0 kullandığımı onayladım ve farklı ortamlarda birçok kez test ettim. This.auth.auth.setPersistence ('none') bile bellek sızıntısını engellemez. Lütfen buradaki kodu kullanarak kendiniz test edin stackblitz.com/edit/angular-zuabzz
TSR

test adımlarınız neler? Tarayıcımın çökmesini görmek için bir gecede bırakmam gerekir mi? Benim durumumda, dinleyici numarası GC vuruşundan sonra her zaman sıfırlanır ve bellek her zaman 160mb'ye geri döner.
Joshua Chan

@TSR çağrı this.auth.auth.setPersistence('none')içinde ngOnInityerine devre dışı kalıcılığı ile yapıcı.
Joshua Chan

@JoshuaChan bir hizmet yöntemini ne zaman çağırmanız önemli? Bir kurucuya enjekte edilir ve vücudunda bulunur. Neden içeri girmeli ngOnInit?
Sergey

@Sergey çoğunlukla en iyi uygulama için. Ancak bu özel durum için, CPU profillemesini her iki arama yöntemi için de çalıştırdım setPersistenceve yapıcıda yapıldıysa, işlev çağrılarının IndexedDB'ye hala yapıldığını, oysa eğer ngOnInityapılırsa, IndexedDB'ye tam olarak değil, emin neden olsa
Joshua Chan
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.