Solution de contournement pour IE10 setInterval Memory Leak

Au cours de l'essai de notre bibliothèque Javascript, nous avons trouvé une grave fuite de mémoire dans l'implémentation Javascript IE10 (v10.0.9200.16519 – Windows 8 64 bits) de setInterval .

Un cas de test simple a montré que si une variable est capturée lors de la fermeture de la fonction passée comme argument pour une exécution ultérieure, elle ne semble jamais devenir éligible pour la collecte des ordures, c'est-à-dire que le navigateur semble toujours tenir une référence à la fonction ou Au moins les variables de fermeture.

Notre testcase exécute la fonction setInterval une seule fois, puis efface la minuterie d'intervalle, c'est-à-dire après un certain temps, aucun code n'est en cours d'exécution et aucune variable n'est plus accessible (pour autant que je voie, aucun globals n'est introduit dans ce code, à l'exception de la méthode à onload ), néanmoins le processus prend environ une demi-gigabyte de mémoire (selon le nombre d'itérations).

Fait intéressant, cela ne se produit pas si nous utilisons plutôt la méthode setTimeout (et le problème ne semble pas exister dans IE9 et les versions actuelles de Chrome, FF).

Le problème peut être vu avec cette violon .

Exécutez-le dans une nouvelle instance d'IE10 sur Windows 8 et ouvrez le gestionnaire de tâches pour surveiller l'utilisation de la mémoire. Il augmentera rapidement à 350 Megabytes et restera là après l'exécution du script.

C'est la partie importante de la pièce de code problématique:

 // the function that when called multiple times will cause the leak in IE10 var eatMemory = function() { var a = null; // the captured closure variable var intervalId = setInterval(function() { a = createBigArray(); // call a method that allocates a lot of memory clearInterval(intervalId); // stop the interval timer }, 100); } 

(Je sais qu'il est facile de réparer ce code spécifique . Mais ce n'est pas le but – c'est juste le plus petit code que nous avons créé, qui reproduit le problème. Le code réel en capte réellement dans la fermeture et cet objet Jamais les ordures ne sont collectées.)

Existe-t-il un bug dans notre code ou existe-t-il un moyen d'utiliser setInterval où une variable de fermeture contient une référence à un objet grand sans déclencher la fuite de la mémoire et sans revenir aux appels setTimeout "récursifs"?

(J'ai également posté la question sur MSDN )

Mise à jour: ce problème existe également dans IE10 sur Windows 7, mais n'existe pas si vous passez en mode IE9-standard. Je l'ai soumis à MS Connect et je signalerai les progrès.

Mise à jour: Microsoft a accepté le problème et l'a signalé pour être corrigé dans IE11 (version d'aperçu) – Je ne l'ai pas encore confirmé moi-même (quelqu'un?)

Mise à jour: IE 11 a été officiellement publié et je ne peux plus reproduire le problème sur cette version avec mon système (Win 8.1 Pro 64bit).

Pour l'exhaustivité, j'ajoute une solution possible ici:

Comme je l'ai déjà écrit (et les commentateurs ont suggéré), cela peut être travaillé (pas corrigé) en retombant sur setTimeout . Ce n'est pas trivial, car il est nécessaire de faire une comptabilité. Voici ma correction proposée, que vous pouvez tester et tirer de cette violon :

 var registerSetIntervalFix = function(){ var _setTimeout = window.setTimeout; var _clearTimeout = window.clearTimeout; window.setInterval = function(fn, interval){ var recurse = function(){ var newId = _setTimeout(recurse, interval); window.setInterval.mapping[returnValue] = newId; fn(); } var id = _setTimeout(recurse, interval); var returnValue = id; while (window.setInterval.mapping[returnValue]){ returnValue++; } window.setInterval.mapping[returnValue] = id; return returnValue; } window.setInterval.mapping = {}; window.clearInterval = function(id){ var realId = window.setInterval.mapping[id]; _clearTimeout(realId); delete window.setInterval.mapping[id]; } } 

L'idée est d'appeler setTimeout pour simuler les appels setInterval récurrents. Il y a un peu de frais généraux dans cette implémentation puisqu'il doit effectuer la tenue de la comptabilité pour l'échange d' id , donc je ne recommanderais pas d'appliquer cette solution, sauf si cela est requis.

Malheureusement, je ne suis pas en mesure de proposer un algorithme de détection "fonctionnalité" (plus comme un algorithme de détection "bug"), alors je suppose que vous devez revenir à une bonne détection de vieux navigateur. De plus, mon implémentation ne peut pas traiter les chaînes comme premier argument et ne pas transmettre d'arguments supplémentaires à la fonction interne. Enfin, il n'est pas sûr d'appeler cette méthode deux fois, alors utilisez-la à vos propres risques (et n'hésitez pas à l'améliorer)!

(Remarque: Pour notre bibliothèque, nous arrêterons d'utiliser setInterval partir de maintenant et setInterval les quelques parties du code qui utilisent setTimeout directement.)