2016-10-04 3 views
1

Nur zum Spaß und um nodejs auszuprobieren, habe ich ein sehr, sehr einfaches Programm geschrieben, das die Collatz-Vermutung auf eine absurde Menge an Zahlen testet. Dies sollte theoretisch gut sein. Das Problem, das ich habe, ist, dass dieser super einfache Code ein Speicherleck hat und ich kann nicht warum feststellen.Speicherlecks in der einfachen nodejs App

var step; 
var numberOfSteps; 
for (var i = 0; i < 100000000000000; i++) { 
    step = i; 
    numberOfSteps = 0; 
    while (step !== 1) { 
     if (step%2 === 0) 
      step /= 2; 
     else 
      step = 3 * step + 1; 
     numberOfSteps++; 
    } 
    console.log("" + i + ": " + numberOfSteps + " steps."); 
} 

Ich habe die Variablen sowohl innerhalb als auch außerhalb der Schleife versucht. Ich habe versucht, sie am Ende der Schleife auf Null zu setzen. Nichts ändert das Speicherleck.

+0

Wo ist das Leck? Ich versuchte es auf meinem Computer und mein Speicher erhöhte nur weniger als 0,01G – Turtle

+2

gibt es kein Leck ... aber die while-Schleife ist ** unendlich ** für Schritt === 0 ... obwohl, wenn Sie das Problem beheben, Knoten scheint langsam Speicher zu verschlingen, nicht wahr –

+2

Es ist die 'console.log', die es verursacht - es ist fast so, als ob der GC nicht in der Lage wäre, den vom console.log zurückgelassenen Müll zu bereinigen –

Antwort

1

ein bisschen mein Coredump Untersuchung:

<--- Last few GCs ---> 

    131690 ms: Scavenge 1398.1 (1458.1) -> 1398.1 (1458.1) MB, 1.3/0 ms (+ 2.8 ms in 1 steps since last GC) [allocation failure] [incremental marking delaying mark-sweep]. 
    132935 ms: Mark-sweep 1398.1 (1458.1) -> 1398.1 (1458.1) MB, 1245.0/0 ms (+ 3.7 ms in 2 steps since start of marking, biggest step 2.8 ms) [last resort gc]. 
    134169 ms: Mark-sweep 1398.1 (1458.1) -> 1398.1 (1458.1) MB, 1234.5/0 ms [last resort gc]. 


<--- JS stacktrace ---> 

==== JS stack trace ========================================= 

Security context: 0x33083d8e3ac1 <JS Object> 
    1: /* anonymous */ [/user/projects/test.js:~1] [pc=0x557d307b271] (this=0x2a4a669d8341 <an Object with map 0xf8593408359>,exports=0x33083d804189 <undefined>,require=0x33083d804189 <undefined>,module=0x33083d804189 <undefined>,__filename=0x33083d804189 <undefined>,__dirname=0x33083d804189 <undefined>) 
    3: _compile [module.js:413] [pc=0x557d304d03c] (this=0x2a4a669d8431... 

FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - process out of memory 
Aborted (core dumped) 

Es scheint, dass dies ein bekanntes Problem zu console.log ist nach dieser Ausgabe auf Github https://github.com/nodejs/node/issues/3171

This is a known "issue" since writing to stdout in the case of a tty/console is async. So logging a lot of data very fast could very well cause a lot of writes to be buffered in memory if the tty/console cannot keep up.

1

Hier ist Code, der scheint Spitze bei etwa 50 MB für mich

Dies führt die Funktion in vielen 10000 - mit einem setImmediate, um die nächste Charge

zu verarbeiten
function collatz(n) { 
    var step,numberOfSteps, i; 
    for(i = 0; i < 10000; i++, n++) { 
     step = n; 
     numberOfSteps = 0; 
     while (step !== 1) { 
      if (step%2 === 0) 
       step /= 2; 
      else 
       step = 3 * step + 1; 
      numberOfSteps++; 
     } 
     console.log("" + n + ": " + numberOfSteps + " steps."); 
    } 
    if (n < 100000000000000) { 
     setImmediate(collatz, n); 
    } 
} 
collatz(1); 

Hinweis, in diesem Fall können Sie die bei 0 für Loop-Start, weil n bei 1 beginnt: p

ich nicht höhere Werte des

ich für Schleife versucht haben, habe etwas Benchmarking gegen den ursprünglichen Code gemacht - das Ausführen von 100 gleichzeitig (in der for-Schleife) ergibt die gleiche Leistung wie 10000 und ist in der Leistung vom ursprünglichen Code nicht zu unterscheiden. Selbst mit 10 auf einmal wäre ich nicht der Meinung, dass diese Methode auch langsamer ist. Nur bei 1 gleichzeitig ist es durchweg 5-8% langsamer als der ursprüngliche Code

Note, I originally thought the issue was garbage collection (or lack thereof) due to the tight loop giving node no time to do any house keeping, but while I was posting the answer, @Svabel posted what seems to be a known issue with hitting console.log hard.

I can only assume that using setImmediate allows some sort of house keeping regarding the tty buffers that is otherwise not possible.

+0

Können Sie erklären, warum asynchron das Speicherproblem löst? – Bergi

+0

nicht wirklich, ich dachte, es könnte ein GC-Problem gewesen sein, aber lesen @ Svabels Antwort, es könnte etwas mit dem "bekannten Problem" in Bezug auf console.log zu tun haben - ich wollte das nicht in meine Antwort als das aufnehmen würde scheinen unpassend –

+1

interessant, ich sehe in diesem "Problem", die Verwendung von 'setImmediate' war eine vorgeschlagene Lösung - ein setImmediate pro Iteration - 10000 pro Iteration ist Overkill, sogar 100 gibt ähnliche (tatsächlich über ein paar Tests kann ich ' t sagen, es ist überhaupt schlimmer) "Leistung" zu dem ursprünglichen Code - während 1 pro Iteration hat etwa 8% Einfluss auf die Geschwindigkeit –