Les meilleures pratiques pour la réduction de l'activité du ramasse-miettes en Javascript
J'ai une assez complexe application Javascript, qui a une boucle principale qui est appelée à 60 fois par seconde. Il semble y avoir beaucoup de déchets de la collection en cours (basé sur les 'dents de scie' la sortie de la chronologie de la Mémoire dans le Chrome dev tools), et cela a souvent des répercussions sur les performances de l'application.
Donc, j'essaie de recherche sur les meilleures pratiques pour la réduction de la quantité de travail que le garbage collector a à faire. (La plupart des informations que j'ai pu trouver sur le web en ce qui concerne évitant les fuites de mémoire, qui est une question légèrement différente - mon espace mémoire est libérée, c'est juste qu'il y a trop de collecte des ordures se passe.) Je suis en supposant qu'il s'agit essentiellement vers le bas pour réutiliser les objets autant que possible, mais bien sûr, le diable est dans les détails.
L'application est structurée en "classes", le long de la lignes de John Resig est Simple JavaScript Héritage.
Je pense à un problème, c'est que certaines fonctions peuvent être appelées des milliers de fois par seconde (car ils sont utilisés des centaines de fois au cours de chaque itération de la boucle principale), et peut-être le travail local des variables dans ces fonctions (chaînes de caractères, tableaux, etc.) peut-être la question.
Je suis au courant de l'objet de mise en commun pour les plus grands ou plus lourds objets (et nous nous en servons pour un degré), mais je suis à la recherche pour les techniques qui peuvent être appliquées à travers le conseil d'administration, notamment en ce qui concerne les fonctions qui sont appelées à de très nombreuses reprises dans les boucles serrées.
Quels moyens puis-je utiliser pour réduire la quantité de travail que le collecteur doit faire?
Et, peut-être aussi - quelles techniques peuvent être employées pour identifier les objets qui sont en train d'être nettoyés le plus? (C'est un farly grande base de code, afin de comparer les clichés du tas n'a pas été très fructueuse)
- Avez-vous un exemple de votre code que vous pourriez nous montrer? La question sera plus facile de répondre ensuite (mais aussi potentiellement moins général, donc je ne suis pas sûr ici)
- Vous pouvez être en mesure d'utiliser et resuse variables à l'extérieur de son champ d'application, de sorte que vous n'avez pas les recréer à chaque fois. Par exemple:
var x = "longstring";
dans votre boucle provoque des milliers de "longsting"s à être créé, mais dans un outerscope il ne fera que provoquer "longstring". - Comment arrêter l'exécution des fonctions des milliers de fois par seconde? Est-ce vraiment la seule façon d'aborder cette question? Cette question semble être un XY problème. Vous décrivez X, mais ce que vous êtes vraiment à la recherche d'une solution pour Y.
- Il l'utilise seulement 60 fois par seconde, ce qui est tout à fait commun vitesse d'animation. Il n'a pas demander de faire moins de travail, mais comment le faire plus garbage-collection-efficace.
- les fonctions peuvent être appelées des milliers de fois par seconde". C'est une fois par milliseconde (éventuellement le pire!). Qui n'est pas commun à tous. 60 fois par seconde ne devrait pas être un problème. Cette question est trop vague et ne va produire des opinions ou des suppositions.
- Il n'y a pas de réponses utiles, mais cette question sonne un peu comme le double possible requestAnimationFrame garbage collection
- merci, mais pour autant que je peux voir que la question est à propos de la requestAnimationFrame appelons la création de déchets (quand ce n'est pas vraiment de traitement de n'importe quoi).
- Ce n'est pas du tout rare dans le jeu de cadres.
Vous devez vous connecter pour publier un commentaire.
Beaucoup de choses que vous devez faire pour minimiser GC taux de désabonnement aller à l'encontre de ce qui est considéré comme idiomatiques JS dans la plupart des autres scénarios, veuillez donc garder à l'esprit le contexte pour juger de l'avis que je donne.
De répartition qui se passe dans moderne interprètes dans plusieurs endroits:
new
ou via la syntaxe littérale[...]
, ou{}
.(function (...) { ... })
.Object(myNumber)
ouNumber.prototype.toString.call(42)
Array.prototype.slice
.arguments
à réfléchir sur la liste de paramètres.Éviter de faire de ceux-ci, et la piscine et la réutilisation d'objets lorsque cela est possible.
En particulier de rechercher des opportunités pour:
split
ou expression régulière correspond depuis chaque nécessite plusieurs allocations d'objets. Cela arrive fréquemment avec les clés dans les tables de recherche et dynamique DOM Id de nœud. Par exemple,lookupTable['foo-' + x]
etdocument.getElementById('foo-' + x)
les deux impliquent une répartition puisqu'il y a une concaténation de chaîne. Souvent, vous pouvez attacher des clés à longue durée de vie des objets au lieu de les re-concaténation. Selon les navigateurs, vous avez besoin de soutien, vous pourriez être en mesure d'utiliserMap
d'utiliser des objets comme des clés directement.try { op(x) } catch (e) { ... }
, neif (!opCouldFailOn(x)) { op(x); } else { ... }
.JSON.stringify
qui utilise un interne tampon natif d'accumuler de contenu au lieu d'affectation de plusieurs objets.arguments
étant donné que les fonctions que l'utilisation que créer un tableau comme objet lorsqu'il est appelé.J'ai suggéré à l'aide de
JSON.stringify
pour créer sortant des messages du réseau. Entrée lors de l'analyse des messages à l'aideJSON.parse
entraîne évidemment de répartition, et beaucoup d'elle pour les grands messages. Si vous pouvez vous le représenter vos messages entrants que des tableaux de primitives, alors vous pouvez économiser beaucoup d'allocations. La seule autre builtin autour de laquelle vous pouvez construire un analyseur qui n'alloue pas estString.prototype.charCodeAt
. Un analyseur syntaxique pour une formule complexe qui n'utilise que va être l'enfer à lire si.JSON.parse
d objets allouer inférieur (ou égal) de l'espace de la chaîne de message?try
entrée, parce que trouver le bloc catch est aussi simple que de trouver le plus étroit protégées de la région contenant le compteur de programme, en faisant une recherche binaire sur un [table] (artima.com/underthehood/exceptions.html) qui peut être retardée jusqu'à ce lancer. Exécutions peuvent différer à savoir si le coût de stockage de la pile est encourue lorsquenew Error()
est fait (comme Java) ou lorsqu'une erreur est levée (comme Python), mais si aucune erreur n'est créé, vous n'avez ni le coût de la création de l'objet ni d'une pile à pied.arr = [a, b];
, savez-vous si il ne il ne en place, ou en créer un nouveau tableau de ramassage des ordures et de recueillir l'ancienne? Serait-il préférable d'utiliserarr[0] = a; arr[1] = b;
?[a, b]
alloue le tableau. Learr = ...
ne les allocations, puisque ce qui est affecté est une référence. Vous devez être prudent au sujet de l'attribution à des éléments d'un tableau lorsque le tableau est partagé avec un autre code. Le javascriptvar arrayX = [0]; var arrayY = arrayX; arrayX[0] = 1; console.log(arrayY);
est assez différente de la superficiellement similaires programme perlmy @arrayX = (0); my @arrayY = @arrayX; $arrayX[0] = 1; print "@arrayY\n";
Les outils de développement Chrome ont une caractéristique très intéressante pour le suivi de l'allocation de mémoire. Cela s'appelle la Chronologie de la Mémoire. Cet article décrit certains détails. Je suppose que c'est ce que vous parlez de la "dent de scie"? Ce comportement est normal pour la plupart des GC ed runtimes. Allocation se poursuit jusqu'à ce qu'un seuil d'utilisation est atteint le déclenchement d'une collection. Normalement, il y a différents types de collections à des seuils différents.
Garbage collections sont inclus dans la liste des événements associés à la trace le long de leur durée. Sur mon vieux portable, éphémère, de la collections sont survenus à environ 4 mo et prendre 30ms. C'est 2 de votre 60Hz itérations de boucle. Si c'est une animation, 30ms collections sont probablement à l'origine de bégayer. Vous devriez commencer ici pour voir ce qui se passe dans votre environnement: lorsque le seuil de collecte est et combien de temps vos collections sont à prendre. Cela vous donne un point de référence pour évaluer les optimisations. Mais vous n'aurez probablement pas faire mieux que de diminuer la fréquence de la bégayer en ralentissant le taux d'attribution, à l'allongement de l'intervalle entre les collections.
L'étape suivante consiste à utiliser les Profils | Record Tas Allocations fonctionnalité permettant de générer un catalogue des allocations selon le type d'enregistrement. Cela va très vite montrer des types d'objet qui consomment le plus de mémoire au cours de la trace de la période, ce qui est équivalent à l'allocation de taux. Focus sur celles-ci dans l'ordre décroissant de leur taux.
Les techniques ne sont pas la science de fusée. Éviter l'encadré d'objets lorsque vous pouvez le faire avec un "unboxed" un. Utiliser des variables globales à détenir et à la réutilisation unique en boîte des objets plutôt que d'allouer des neuves à chaque itération. Piscine commune de types d'objets dans les listes libres plutôt que de les abandonner. Cache de concaténation de chaîne de résultats qui sont susceptibles réutilisables dans les itérations futures. Éviter de répartition juste de revenir résultats de la fonction de définition de certaines variables dans un cadre englobant à la place. Vous aurez à considérer chaque type d'objet dans son propre contexte pour trouver la meilleure stratégie. Si vous avez besoin d'aide avec des détails, de poster un edit décrivant les détails du défi que vous êtes en train de regarder.
Je vous déconseille de pervertir le cours normal de votre style de codage dans une application dans un fusil de chasse pour tenter de produire moins de déchets. C'est pour la même raison, vous ne devriez pas optimiser pour la vitesse prématurément. La plupart de vos efforts plus grande partie de la complexité et de l'opacité de code sera vide de sens.
request animation frame
,animation frame fired
, etcomposite layers
. Je n'ai aucune idée de pourquoi je ne suis pas voyantGC Event
comme vous êtes (c'est sur la dernière version de chrome, et aussi des canaries).@342342
etcode relocation info
.Comme principe général, vous voulez cache autant que possible et de faire ce que peu de création et de destruction pour chaque exécution de la boucle.
La première chose qui apparaît dans ma tête c'est de réduire l'utilisation de fonctions anonymes (si vous en avez) à l'intérieur de ta boucle principale. Aussi, il serait facile de tomber dans le piège de la création et de la destruction des objets qui sont passés dans d'autres fonctions. Je ne suis pas un expert javascript, mais j'imagine que cela:
irait beaucoup plus vite que ce:
Est-il jamais aucun temps d'arrêt de votre programme? Peut-être que vous en avez besoin afin de fonctionner correctement pendant une seconde ou deux (par exemple, pour une animation) et puis, il a plus de temps à traiter? Si c'est le cas, j'ai pu voir de prendre des objets qui ne seraient normalement pas les ordures collectées tout au long de l'animation et de garder une référence à eux dans certains objet global. Puis, quand l'animation se termine, vous pouvez effacer toutes les références et de laisser le garbage collector ne c'est du travail.
Désolé si c'est un peu trivial par rapport à ce que vous l'avez déjà fait et pensé.
Je ferais un ou quelques objets dans la
global scope
(où je suis sûr que le garbage collector n'est pas autorisé à y toucher), alors je vais essayer de revoir ma solution pour l'utilisation de ces objets pour faire le travail, au lieu d'utiliser des variables locales.Bien sûr, il ne pouvait pas se faire partout dans le code, mais en général, c'est ma façon à éviter le garbage collector.
P. S. Il peut faire que la partie spécifique de code un peu moins facile à gérer.