javascript - stop - typescript setinterval




Führt die JavaScript-Methode setInterval() zu Speicherverlusten? (6)

Bei jedem Funktionsaufruf wird ein Stack-Frame erstellt . Im Gegensatz zu vielen anderen Sprachen speichert Javascript den Stapelrahmen auf dem Heap, genau wie alles andere. Dies bedeutet, dass jedes Mal, wenn Sie eine Funktion aufrufen, die Sie alle 50 ms ausführen, ein neuer Stack-Frame zum Heap hinzugefügt wird. Dies summiert sich und wird schließlich Müll gesammelt.

Es ist irgendwie unvermeidlich, wie Javascript funktioniert. Das Einzige, was wirklich getan werden kann, ist, die Stack-Frames so klein wie möglich zu machen, was sicher alle Implementierungen tun.

Derzeit wird ein JavaScript-basiertes Animationsprojekt entwickelt.

Ich habe festgestellt, dass bei richtiger Verwendung von setInterval() , setTimeout() und sogar requestAnimationFrame Speicher ohne meine Anforderung zugewiesen wird und häufige Garbage Collection-Aufrufe verursacht werden. Weitere GC-Aufrufe = Flimmern :-(

Zum Beispiel; Wenn ich den folgenden einfachen Code durch Aufruf von init () in Google Chrome ausführe, ist die Speicherzuordnung + Speicherbereinigung für die ersten 20 bis 30 Sekunden in Ordnung ...

function init()
{
    var ref = window.setInterval(function() { draw(); }, 50);
}

function draw()
{
    return true
}

Irgendwie beginnt innerhalb einer Minute oder so eine merkwürdige Vergrößerung des zugewiesenen Speichers! Was ist der Grund für die Erhöhung der zugewiesenen Speichergröße, da init () nur einmal aufgerufen wird?

(Bearbeiten: Chrome-Screenshot hochgeladen)

HINWEIS 1: Ja, ich habe versucht, clearInterval () vor dem nächsten setInterval () aufzurufen. Problem bleibt gleich!

Anmerkung 2: Um das Problem einzugrenzen, halte ich den obigen Code einfach und dumm.


Chrome sieht kaum Speicherdruck in Ihrem Programm (1,23 MB sind nach heutigen Maßstäben sehr wenig Speicherverbrauch), so dass es wahrscheinlich nicht der Meinung ist, dass es aggressiv GC-basiert. Wenn Sie Ihr Programm ändern, um mehr Speicherplatz zu verwenden, wird der Garbage Collector aktiviert. Versuchen Sie dies beispielsweise:

<!html>
<html>
<head>
<title>Where goes memory?</title>
</head>
<body>

Greetings!

<script>
function init()
{
    var ref = window.setInterval(function() { draw(); }, 50);
}

function draw()
{
    var ar = new Array();
    for (var i = 0; i < 1e6; ++i) {
        ar.push(Math.rand());
    }
    return true
}

init();
</script>

</body>
</html>

Wenn ich das ausführen, bekomme ich ein Sägezahngedächtnis-Verwendungsmuster, das unter 13,5 MB (wieder recht klein für heutige Verhältnisse) liegt.

PS: Besonderheiten meiner Browser:

Google Chrome   23.0.1271.101 (Official Build 172594)
OS  Mac OS X
WebKit  537.11 (@136278)
JavaScript  V8 3.13.7.5
Flash   11.5.31.5
User Agent  Mozilla/5.0 (Macintosh; Intel Mac OS X 10_8_2) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.101 Safari/537.11

EDIT: Yury Antwort ist besser.

tl; dr IMO gibt es keinen Speicherverlust. Die positive Steigung ist einfach der Effekt von setInterval und setTimeout. Der Müll wird durch Sägezahnmuster gesammelt, was bedeutet, dass es keinen Speicherverlust gibt. (Meiner Ansicht nach).

Ich bin mir nicht sicher, ob es einen Weg gibt, um dieses sogenannte "Memory Leak" zu umgehen. In diesem Fall bezieht sich "Speicherverlust" auf jeden Aufruf der Funktion setInterval, wodurch die Speichernutzung erhöht wird, wie durch die positiven Flanken im Speicherprofiler angezeigt wird.

Die Realität ist, dass es keinen tatsächlichen Speicherverlust gibt: Der Garbage Collector kann den Speicher immer noch sammeln. Speicherverlust per Definition "tritt auf, wenn ein Computerprogramm Speicher abruft, ihn jedoch nicht wieder an das Betriebssystem freigibt."

Wie aus den folgenden Speicherprofilen hervorgeht, tritt kein Speicherverlust auf. Die Speicherbelegung steigt mit jedem Funktionsaufruf. Das OP erwartet, dass es keine Speichererweiterung geben sollte, da dies die gleiche Funktion ist, die immer wieder aufgerufen wird. Dies ist jedoch nicht der Fall. Bei jedem Funktionsaufruf wird Speicher belegt. Schließlich wird der Müll gesammelt und das Sägezahnmuster erzeugt.

Ich habe verschiedene Möglichkeiten zur Neuordnung der Intervalle ausprobiert, und alle führen zu demselben Sägezahnmuster (obwohl einige Versuche dazu führen, dass die Speicherbereinigung nie auftritt, da die Referenzen beibehalten werden).

function doIt() {
    console.log("hai")
}

function a() {
    doIt();
    setTimeout(b, 50);
}
function b() {
    doIt();
    setTimeout(a, 50);
}

a();

http://fiddle.jshell.net/QNRSK/14/

function b() {
    var a = setInterval(function() {
        console.log("Hello");
        clearInterval(a);
        b();                
    }, 50);
}
b();

http://fiddle.jshell.net/QNRSK/17/

function init()
{
    var ref = window.setInterval(function() { draw(); }, 50);
}
function draw()
{
    console.log('Hello');
}
init();

http://fiddle.jshell.net/QNRSK/20/

function init()
{
    window.ref = window.setInterval(function() { draw(); }, 50);
}
function draw()
{
    console.log('Hello');
    clearInterval(window.ref);
    init();
}
init();​

http://fiddle.jshell.net/QNRSK/21/

Anscheinend sind setTimeout und setInterval nicht offiziell Teil von Javascript (daher auch nicht Teil von v8). Die Implementierung bleibt dem Implementierer überlassen. Ich schlage vor, Sie schauen sich die Implementierung von setInterval und so in node.js an


Es scheint kein Speicherverlust zu sein. Solange die Speicherbelegung nach GC wieder abnimmt und die durchschnittliche Speicherbelegung im Durchschnitt nicht nach oben tendiert, gibt es kein Leck.

Die "echte" Frage, die ich hier setInterval ist, dass setInterval tatsächlich Speicher verwendet, um zu funktionieren, und es sieht nicht so aus, als würde es irgendetwas setInterval . In der Realität muss es einige Dinge zuordnen:

  1. Es muss etwas Stapelspeicherplatz zugewiesen werden, um sowohl die anonyme Funktion als auch die draw () - Routine auszuführen.
  2. Ich weiß nicht, ob temporäre Daten zugewiesen werden müssen, um die Anrufe selbst auszuführen (wahrscheinlich nicht).
  3. Es muss eine kleine Menge Speicherplatz zugewiesen werden, um den true Rückgabewert von draw() speichern.
  4. Intern kann setInterval zusätzlichen Speicher zuweisen, um ein wiederkehrendes Ereignis erneut zu planen (ich weiß nicht, wie es intern funktioniert, es kann den vorhandenen Datensatz wiederverwenden).
  5. Die JIT versucht möglicherweise, diese Methode zu verfolgen, wodurch zusätzlicher Speicher für die Ablaufverfolgung und einige Metriken zugewiesen werden. Die VM stellt möglicherweise fest, dass diese Methode zu klein ist, um sie zu verfolgen. Ich weiß nicht genau, welche Schwellenwerte für das Aktivieren oder Deaktivieren der Ablaufverfolgung gelten. Wenn Sie diesen Code lange genug ausführen, um von der VM als "heiß" erkannt zu werden, kann der JIT-kompilierte Maschinencode noch mehr Speicher enthalten (danach würde ich erwarten, dass die durchschnittliche Speicherbelegung abnimmt, da der generierte Maschinencode sollte in den meisten Fällen weniger Speicherplatz zuweisen)

Bei jeder Ausführung Ihrer anonymen Funktion wird Speicherplatz zugewiesen. Wenn sich diese Zuweisungen auf einen bestimmten Schwellenwert summieren, tritt der GC ein und bereinigt sich, um Sie wieder auf ein Basisniveau zu bringen. Der Zyklus wird so fortgesetzt, bis Sie ihn abschalten. Dies ist erwartetes Verhalten.


Ich wollte auf Ihren Kommentar zu setInterval und flackern antworten:

Ich habe festgestellt, dass bei richtiger Verwendung von setInterval (), setTimeout () und sogar requestAnimationFrame Speicher ohne meine Anforderung zugewiesen wird und häufige Garbage Collection-Aufrufe verursacht werden. Weitere GC-Aufrufe = Flimmern :-(

Möglicherweise möchten Sie versuchen, den setInterval-Aufruf durch eine weniger schädliche, selbstaufrufende Funktion zu ersetzen, die auf setTimeout basiert. Paul Irish erwähnt dies in dem Vortrag 10 Dinge, die ich aus der jQuery-Quelle gelernt habe (Video here , Notizen here siehe Nr. 2). Sie ersetzen Ihren Aufruf von setInterval durch eine Funktion, die sich indirekt über setTimeout aufruft, nachdem er die Arbeit beendet hat, die er ausführen soll . Um den Vortrag zu zitieren:

Viele haben argumentiert, dass setInterval eine böse Funktion ist. Sie ruft eine Funktion in festgelegten Intervallen auf, unabhängig davon, ob die Funktion beendet ist oder nicht.

Mit Ihrem Beispielcode oben können Sie Ihre Init-Funktion folgendermaßen aktualisieren:

function init() 
{
    var ref = window.setInterval(function() { draw(); }, 50);
}

zu:

function init()
{
     //init stuff

     //awesome code

     //start rendering
     drawLoop();
}

function drawLoop()
{
   //do work
   draw();

   //queue more work
   setTimeout(drawLoop, 50);
}

Das sollte ein bisschen helfen, weil:

  1. draw () wird von Ihrer Rendering-Schleife erst wieder aufgerufen, wenn sie abgeschlossen ist
  2. Wie viele der obigen Antworten darauf hinweisen, haben alle ununterbrochenen Funktionsaufrufe von setInterval einen Overhead im Browser.
  3. Das Debuggen ist etwas einfacher, da Sie durch das fortgesetzte Auslösen von setInterval nicht unterbrochen werden

Hoffe das hilft!


Versuchen Sie dies ohne die anonyme Funktion. Zum Beispiel:

function draw()
{
    return true;
}

function init()
{
    var ref = window.setInterval(draw, 50);
}

Verhält es sich immer noch genauso?





setinterval