2013-05-08 21 views
18
function(foo, cb) { 
    var bigObject = new BigObject(); 
    doFoo(foo, function(e) { 
    if (e.type === bigObject.type) { 
      cb(); 
      // bigObject = null; 
    } 
    }); 
} 

Powyższy przykład pokazuje klasyczne, przypadkowe (lub może nie) wyciek pamięci. Śmieciarka V8 nie może określić, czy można bezpiecznie usunąć bigObject, ponieważ jest on używany w funkcji wywołania zwrotnego, które można wywołać kilka razy.Brak wycieku pamięci wywołania zwrotnego w javascript

Jednym rozwiązaniem jest ustawienie bigObject na null, gdy zadanie w funkcji wywołania zwrotnego się zakończy. Ale jeśli używasz wielu zmiennych (wyobraź sobie, że istnieją n zmienne takie jak bigObject i wszystkie są używane w oddzwanianiu), to czyszczenie tego staje się brzydkim problemem.

Moje pytanie brzmi: czy istnieje inny sposób czyszczenia tych używanych zmiennych?

EDYTOWANIE Oto kolejny przykład (rzeczywisty świat): Dostaję więc aplikację od mongodb i porównuję ją z inną aplikacją. Oddzwonienie z mongodb wykorzystuje zmienną aplikację, która jest zdefiniowana z tego wywołania zwrotnego. Po otrzymaniu wyniku od mongodb zwracam go również jako callback (ponieważ jest to wszystko asynchroniczne i nie mogę po prostu napisać return). Tak rzeczywiście może się zdarzyć, że propagują callback całą drogę do źródła ...

function compareApplications(application, condition, callback) { 

    var model = database.getModel('Application'); 
    model.find(condition, function (err, applicationFromMongo) { 
     var result = (applicationFromMongo.applicationID == application.applicationID) 
     callback(result)   
    } 
} 
+0

Zadam wam to - dlaczego jest to problem ? Polecenie 'change' powinno być wywoływane kilka razy. Więc jak Ty (lub GC) kiedykolwiek dowiesz się, kiedy naprawdę skończy się używanie 'bigObject', chyba że odłączysz wydarzenie' change'? Wydaje się, że chcesz jedną instancję 'bigObject', aby program obsługi mógł porównywać typy. Tworzysz go raz, co zmniejsza ładunek za każdym razem, gdy uruchamia się program obsługi. Jeśli chcesz, aby to zostało wyczyszczone, stwórz je w programie obsługi za każdym razem lub spodziewamy się, że "wycieknie" pamięć, ponieważ tak to działa. – Ian

+0

Co powiesz na użycie .one() zamiast .on()? – frenchie

+0

Proszę zauważyć, że zmieniłem przykład. W moim programie na świecie nie używam .on. Przekażę funkcję zwrotną do innej. –

Odpowiedz

1

Jeśli funkcja zwrotna ma jedynie na miano raz, to należy wypisać po to się nazywa. To zwolni twoje oddzwanianie + zamknięcie do GC. Po zamknięciu Twojego konta, GC może również bezpłatnie odebrać bigObject.

To najlepsze rozwiązanie - jak zauważyłeś, GC nie wie w magiczny sposób, że twoje oddzwanianie zostanie wywołane tylko raz.

+2

Dziękuję za odpowiedź, ale czy możesz podać przykład rezygnacji z subskrypcji? –

+1

To zależy od tego, jakiego mechanizmu używasz do subskrybowania wywołania zwrotnego. Nie podajesz wystarczających informacji na temat tego, co robi 'doFoo()'. Większość frameworków udostępnia metodę 'undoFoo()', lub 'doFoo()' samo zwraca metodę, którą możesz wywołać, aby anulować subskrypcję. Jeśli korzystałeś z jQuery, zasubskrybowałeś '$ (" ... "). On (" change ", cb);' i zrezygnuj z '$ (" ... "). Off (" change ", cb) '. W rzeczywistości mają metodę dokładnej sytuacji, w której chcesz subskrybować tylko jedno wydarzenie: '$ (" ... "). Jeden (" zmień ", cb)', który automatycznie zrezygnuje z subskrypcji po pojedynczym połączeniu. – Brandon

+1

Proszę zauważyć, że zmieniłem przykład w pierwszym poście. To jest teraz bardziej jak mój rzeczywisty problem. Przekażę funkcję zwrotną do innej funkcji. Używam node.js. Foo jest tylko przykładem, ale problem jest taki sam jak w moich rzeczywistych funkcjach. Funkcja oddzwaniania używa zmiennych w zakresie i GC nie może tego wyczyścić niejawnie :(... moja pamięć rss bardzo szybko rośnie, ponieważ mam duży ruch –

0

Aby zbudować na odpowiedź Brandona: Jeśli (z jakiegoś strasznego powodu) nie jesteś w stanie wypisać swoje zwrotnego zawsze można obsługiwać usunięcie zwrotnego siebie:

function createSingleUseCallback(callback) 
{ 
    function callbackWrapper() 
    { 
     var ret = callback.apply(this, arguments); 
     delete callback; 
     return ret; 
    } 
    return callbackWrapper; 
} 

function compareApplications(application, condition, callback) 
{ 
    var model = database.getModel('Application'); 
    model.find(condition, createSingleUseCallback(function (err, applicationFromMongo) 
    { 
     var result = (applicationFromMongo.applicationID == application.applicationID); 
     callback(result); 
    }) 
} 
Powiązane problemy