2017-07-15 4 views
1

J'essaie de déterminer si une fuite de mémoire peut être créée par une fonction asynchrone qui ne reprend jamais. Par exemple:Comprendre Chrome GC avec des méthodes asynchrones

function timeout(ms) { 
    return new Promise(resolve => setTimeout(resolve, ms)); 
    } 

    function Test() { 
    this.arr = []; 

    this.alloc = async function() { 
     for (i = 0; i < 300000; i++) { 
     this.arr.push(document.createElement('div')); 
     } 
     await timeout(10000); 
     alert('done waiting ' + this.arr.length); // outputs 300000 
    }; 
    }; 

    var test = new Test(); 

    function leak() { 
    test.alloc(); 
    test = null; 
    window.gc(); // Running chrome with expose-gc flag 
    // Snapshotting memory here shows divs have been deallocated 
    } 

Utilisation des outils de mémoire de Chrome, je pris un instantané lorsque ce code se termine l'exécution. Je m'attendais à voir 300000 HTMLDivElements encore alloués, mais voilà, la mémoire semble "libérée". Ce qui est étrange, c'est si j'essaie d'accéder au contenu du tableau, ils sont toujours là. Quelqu'un peut-il expliquer ce phénomène?

+0

Où le drapeau 'expos-gc' est-il documenté? – guest271314

+0

Si gc agit bizarrement et est affectée par l'affichage de 'console.log' (ou' alert') étant présent ou non, je devine qu'un 'await' permet les mêmes [optimisations de portée que les fermetures obtiennent] (https://stackoverflow.com/questions/28388530/why-does-chrome-debugger-think-closed-local-variable-is-undefined). – Bergi

Répondre

1

Si je remplace someForeverPromise avec une minuterie et puis essayez d'accéder au contenu du tableau, ils sont toujours là

Votre someForeverPromise lui-même était ramasse-miettes, et avec elle tous les callbacks qui attendaient encore il - y compris la reprise du async function.

Vous pouvez utiliser

const foreverPendingPromise = new Promise(resolve => { 
    global.reference = resolve; 
}); 

où l'reference mondiale tient une façon de résoudre la promesse d'empêcher les callbacks d'être des déchets collectés. (Vous devez également vous assurer que personne n'appelle accidentellement reference pour s'assurer qu'il reste en attente, mais je laisserai cela comme exercice au lecteur).

+0

C'est étrange, parce que si je remplace la promesse par une minuterie et que j'ajoute une alerte, elle apparaît toujours. – Dandan

+0

@Dandan Un temporisateur garde aussi une référence globale à son rappel ('resolve'?) Ainsi – Bergi

+0

J'ai utilisé l'implémentation suivante: function timeout (ms) { return new Promise (résolution => setTimeout (résolution, ms)) ; } Maintenant, si j'attends le délai d'attente (10000), le profileur affiche la mémoire comme libérée mais je reçois toujours l'alerte. – Dandan