2012-12-28 10 views
6

Mam aplikację, w której sekwencyjnie pobierane są pliki mp3 z serwera, tymczasowo przechowuję je na moim serwerze, a następnie przesyłam je bezpośrednio do klientów, na przykład:NodeJS: Jak zwolnić bufory przydzielane poza stosem pamięci V8

function downloadNextTrack(){ 
    var request = http.get('http://mp3server.com', function(response){ 
    response.on('data', function(data) { 
     fs.appendFile('sometrack.mp3', data, function (err) {}); 
    }); 
    response.on('end', function(){ 
     streamTrack('sometrack.mp3'); 
    } 
    }); 
}; 

var clients = []; // client response objects are pushed to this array when they request the stream through a route like /stream.mp3 

var stream; 

function streamTrack(track){ 
    stream = fs.createReadStream(track); 
    stream.on('data', function(data){ 
    clients.forEach(function(client) { 
     client.write(data); 
    }); 
    }); 
    stream.on('end', function(){ 
    downloadNextTrack(); // redoes the same thing with another track 
    } 
}; 

Widocznie ten kod jest tworzenie wielu buforów, które nie są uwolnione przez system operacyjny, kiedy uruchomić polecenie „wolny -m”, to co mam (po około 4 godzinach, uruchamiając aplikację):

    total  used  free  shared buffers  cached 
       Mem: 750  675   75   0   12  180 
-/+ buffers/cache:   481  269 
      Swap: 255  112  143 

Liczba pod "buforami" stale rośnie (jak również cach ed pamięci) i system operacyjny nie odzyskuje tego z powrotem o 180 Mb, aż w końcu moja aplikacja wyczerpuje pamięć i ulega awarii, gdy próbuję utworzyć mały proces, aby zweryfikować bitrate ścieżki, częstotliwość próbkowania, informacje o id3, itp.

Zdiagnozowałem wiele różnych narzędzi (takich jak memwatch i nodetime), aby dowiedzieć się, czy był to wyciek pamięci wewnętrznej, a nie jest, stertę pamięci V8, a także Node RSS różnią się +/- 10mb, ale pozostają stałe przez większość czasu pamięć wolna od systemu OS staje się coraz niższa (kiedy rozpoczyna się proces Node, mam około 350 MB wolnej pamięci).

Przeczytałem gdzieś, że Buforowe instancje przydzielone przez Węzeł mają bezpośredni dostęp do surowej pamięci i dlatego V8 nie ma nad nimi mocy (co sprawdza się z tym, że nie dostaję wycieków pamięci z sterty V8), Rzecz w tym, że potrzebuję sposobu na pozbycie się tych starych buforów. czy to możliwe? Czy będę musiał ponownie uruchomić moją aplikację co 5 godzin (lub, co gorsza, kupić więcej pamięci RAM!)?

PS. Używam Node v0.8.16 na Ubuntu 10.04.

+1

Witam, może to głupie pytanie, ale mnie poprawić jeśli się mylę: kolejność jest 0) pobrać torze # 0 1), gdy wykończenie, strumień dla wszystkich cliends 2) kiedy strumień wykończenie, dwonload Track # 1 Kiedy to robisz, jest to rodzaj rekursji, prawda? w pierwszym wywołaniu 'streamTrack' masz zmienną strumieniową (jest to zmienna globalna?), a na końcu wywołujesz "downloadNextTrack" i ponownie wywołujesz "streamTrack", z inną zmienną strumienia. czy wywołanie za pomocą rekursji może być źródłem problemów? –

+0

W kodzie produkcyjnym ta zmienna strumienia działa bardziej jak singleton, więc gdy rozpoczyna się inny strumień, jest ponownie przypisywany do nowego strumienia ReadStream. Działa jak zmienna globalna, ale nie jest zmienną globalną per se. – pedromtavares

+0

Spróbuj użyć 'setTimeout (function() {streamTrack ('somefile');}, 0);'. To zabije błąd "recursivity" (wiem, trochę opóźniona odpowiedź). –

Odpowiedz

2

Zgadzam się z Tiago, Myślę, że jest to spowodowane rekurencyjnym charakterem Twojego kodu. Nie sądzę, że strumienie są tym, co pożera twoją stertę, ponieważ, jak powiedziałeś, zmienna strumienia jest ponownie przypisywana nowym ReadStreamem z każdą iteracją. Jednak żądanie i odpowiedź http.get (i wszystkie bufory, których używają) w wierszu 2, nigdy nie są zwalniane przed wywołaniem kolejnej iteracji; są one dostępne w ramach funkcji downloadNextTrack. Kończy się rekursywny ślad stosu, który ma zbiór obiektów żądania i odpowiedzi (i niektórych buforów bazowych) dla jednego pliku.

Ogólnie, jeśli ten kod musi działać wiele razy, dlaczego nie zrezygnować z rekurencji i zrobić to wszystko w sposób ciągły? niekończąca się rekurencja zawsze będzie pożerać coraz więcej pamięci, aż program się zawiesza, nawet jeśli nie ma wycieków pamięci z twojej strony.

+0

Sugerujecie więc, że przed wywołaniem streamTrack należy zerować zmienne żądania i odpowiedzi. Sądziłem, że gdy reakcja dobiegnie końca i nie będę już używać tych zmiennych, zostaną one ostatecznie zebrane przez GC. Czy zachowanie wszystkiego w nieskończonej pętli rozwiązałoby to? FYI: to jest kod produkcyjny: https://github.com/pedromtavares/radio/blob/master/lib/provider.js – pedromtavares

+0

Nulling nie pomoże, ponieważ ślad stosu rośnie i rośnie, a JS nadal pozostanie [zamknięcie] odniesienia do obiektów strumienia. Użycie nieskończonej pętli (prostej iteracyjnej pętli) będzie działało, ponieważ wtedy odwoływałoby się do starszych obiektów strumienia. –

+0

Po pewnym zastanowieniu twój sposób myślenia ma sens. Nie mam czasu, aby od razu skorygować kod (i nie mam na to żadnych testów, wstyd mi), aby sprawdzić, czy to rzeczywiście rozwiąże problem, ale uznaję twoją odpowiedź za poprawną. Dzięki człowieku :) – pedromtavares

0

Przeczytaj to: http://www.linuxatemyram.com

cache jest pamięć podręczna dla węzłów i dentry (struktury systemu plików). Ta pamięć jest wciąż dostępna dla procesów. Nie powinieneś o to dbać.

+1

Podczas gdy ten link może odpowiedzieć na pytanie, lepiej umieścić tutaj istotne części odpowiedzi i podać link do odniesienia. Odpowiedzi dotyczące linków mogą stać się nieprawidłowe, jeśli strona z linkami się zmieni. - [Z recenzji] (/ opinia/niskiej jakości-posts/11470551) –

+0

OK, po prostu angielski nie jest moim pierwszym językiem, a strona wyjaśnia to znacznie lepiej. Nie wiem też, czy kopiowanie i wklejanie ich treści jest dozwolonym użyciem. Jedynym celem tej witryny jest wyjaśnienie tego. – arboreal84