2012-12-28 2 views
6

서버에서 mp3 파일을 순차적으로 다운로드하여 임시로 서버에 저장 한 다음 클라이언트에 직접 스트리밍하는 응용 프로그램이 있습니다. 나는 '자유 -M'명령을 실행할 때NodeJS : 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 
    } 
}; 

분명히이 코드는 OS에 의해 해제되지 않는 버퍼를 많이 만드는 것, 이것이 내가 응용 프로그램을 실행하는 약 4 시간 후 (무엇을 얻을) :

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

'버퍼'아래의 숫자는 지속적으로 증가합니다 (캐시 에드 메모리) 그리고 운영체제는 결국 180 메가 바이트를 되찾지 못하게 될 것입니다. 결국 내 메모리가 다 떨어지고 트랙의 비트 전송률, 샘플링 속도, ID3 정보 등을 확인하기 위해 작은 프로세스를 생성하려고 할 때까지 충돌이 발생합니다.

나는 내부 메모리 누출인지 아닌지를 알아 내기 위해 memwatch와 nodetime과 같은 많은 도구를 가지고 진단했다. V8 메모리 힙과 Node RSS는 +/- 10mb가 다르지만 일정하게 유지된다. 대부분의 경우 OS free 메모리는 더 낮아지고 낮아집니다 (Node 프로세스가 시작될 때 약 350MB의 여유 메모리가 있음).

노드에 의해 할당 된 버퍼 인스턴스가 원시 메모리에 직접 액세스 할 수 있으므로 V8에 전원이 공급되지 않아 V8 힙에서 메모리 누수가 발생하지 않는 것으로 확인됩니다. 사실,이 오래된 버퍼를 제거 할 방법이 필요합니다. 이것이 가능한가? 아니면 5 시간마다 앱을 다시 시작해야합니까 (아니면 더 많은 RAM을 구입해야합니까).

추신. Ubuntu 10.04에서 Node v0.8.16을 실행하고 있습니다.

+1

안녕하세요, 아마도 이것이 내가 틀렸다면 정정 해줘, 바보 같은 질문이지만 : 순서가 0) 트랙 번호를 다운로드 0 1) 할 때 모든 cliends 2) 마무리 스트림, dwonload에 대한 마무리, 스트림 트랙 # 1 이렇게하면 일종의 재귀입니다, 맞습니까? 첫 번째 호출 'streamTrack'안에는 스트림 변수 (전역 변수)가 있으며 결국에는 'downloadNextTrack'을 호출하고 'streamTrack'을 다른 스트림 변수로 다시 호출합니다. 재귀를 사용하여 호출하면 문제의 원인이 될 수 있습니까? –

+0

프로덕션 코드에서이 스트림 변수는 싱글 톤 (singleton)과 비슷하게 동작하므로 다른 스트림이 시작되면 새로운 readStream에 다시 할당됩니다. 그것은 전역 변수와 같은 역할을하지만 전역 변수 자체는 아닙니다. – pedromtavares

+0

'setTimeout (function() {somefile '};}, 0);'을 사용해보십시오. 그것은 "재귀"오류를 죽일 것이다 (나는 조금 지연된 대답을 알고있다) –

답변

2

나는 Tiago에 동의합니다. 이것은 코드의 재귀 적 특성 때문에 발생한다고 생각합니다. 당신이 말했듯이, 스트림 변수가 모든 반복마다 새로운 ReadStream으로 재 할당되기 때문에 스트림이 힙을 어떻게 처리하는지는 생각하지 않습니다. 그러나 두 번째 줄에서 http.get의 요청과 응답 (그리고 그들이 사용하는 버퍼)은 다음 반복을 호출하기 전에 해제되지 않습니다. 그것들은 downloadNextTrack 함수 내에서 범위가 지정됩니다. 파일 당 요청 및 응답 객체 (및 일부 기본 버퍼)가있는 재귀 적 스택 추적으로 끝납니다.

일반적으로이 코드를 여러 번 실행해야하는 경우 재귀를 선택 해제하지 않고 반복적으로 수행해야하는 이유는 무엇입니까? 끊임없이 반복되는 재귀는 프로그램이 충돌 할 때까지 메모리 누수가 없더라도 항상 더 많은 메모리를 차지할 것입니다.

+0

그래서 당신은 streamTrack을 호출하기 전에 요청과 응답 변수를 nulling하는 것이 좋을까? 일단 응답이 끝나고 그 변수를 더 이상 사용하지 않으면 GC로 결국 수집하게 될 것이라고 생각했습니다. 무한 루프의 모든 문제를 해결하면 해결할 수 있을까요? FYI : 이것은 프로덕션 코드입니다. https://github.com/pedromtavares/radio/blob/master/lib/provider.js – pedromtavares

+0

스택 추적이 증가하고 커지기 때문에 널링이 도움이되지 않습니다. JS는 [클로저] 스트림 객체에 대한 참조. 무한 루프 (간단한 반복 루프)를 사용하면 트릭이 수행됩니다. 그 이유는 이전 스트림 객체를 참조하기 때문입니다. –

+0

약간의 사고 후에, 당신의 생각의 라인은 실제로 의미가 있습니다. 현재 코드를 리팩터링 할 시간이 없다. (나는 그것에 대한 테스트를하지 않는다. 내게 수치 스럽다.) 실제로 문제를 해결할 수 있는지 알아보기 위해,하지만 나는 당신의 답을 옳다고 생각할 것이다. 고마워요. :) – pedromtavares

0

이 읽기 ​​: http://www.linuxatemyram.com

버퍼 캐시는 아이 노드와 dentries (파일 시스템 구조)에 대한 캐시입니다. 이 메모리는 프로세스에서 계속 사용할 수 있습니다. 당신은 이것에 신경 쓰지 않아야합니다.

+1

이 링크가 질문에 대답 할 수도 있지만 답변의 핵심 부분을 여기에 포함시키고 참조 용 링크를 제공하는 것이 좋습니다. 링크 된 페이지가 변경되면 링크 전용 답변이 유효하지 않게 될 수 있습니다. - [리뷰에서] (리뷰/저품절 포스트/11470551) –

+0

괜찮 았어, 그건 영어가 나의 모국어가 아니기 때문에 그 사이트가 훨씬 더 잘 설명해 준다. 또한 콘텐츠를 복사하여 붙여 넣는 것이 정당한 사용인지 알아야합니다. 그 사이트의 유일한 목적은 그 것을 설명하는 것입니다. – arboreal84

관련 문제