Débordement de mémoire: nombre croissant de Microsoft.CSharp.RuntimeBinder.Semantics

Nous recherchons actuellement des memory leaks dans notre application. Lors de certaines opérations (chargement et fermeture d’un projet dans notre application), nous soaps que la mémoire augmente toujours un peu.

Nous en avons déjà trouvé beaucoup, mais maintenant, les 10 classes les plus croissantes sont (selon notre outil, ANTS Memory Profiler 8.2):

  • Microsoft.CSharp.RuntimeBinder.Semantics.SYMTBL + clé
  • Microsoft.CSharp.RuntimeBinder.Semantics.LocalVariableSymbol
  • Microsoft.CSharp.RuntimeBinder.Semantics.CONSTVAL
  • Microsoft.CSharp.RuntimeBinder.Semantics.EXPRCONSTANT
  • Microsoft.CSharp.RuntimeBinder.Semantics.EXPRCLASS
  • Microsoft.CSharp.RuntimeBinder.Semantics.EXPRTYPEOF
  • Microsoft.CSharp.RuntimeBinder.Semantics.EXPRLIST
  • Microsoft.CSharp.RuntimeBinder.Semantics.MethWithInst
  • Microsoft.CSharp.RuntimeBinder.Semantics.CMemberLookupResults
  • Microsoft.CSharp.RuntimeBinder.Semantics.EXPRMEMGRP
  • Microsoft.CSharp.RuntimeBinder.Semantics.EXPRCALL
  • Microsoft.CSharp.RuntimeBinder.Semantics.EXPRWRAP
  • Microsoft.CSharp.RuntimeBinder.Semantics.AggregateDeclaration
  • Microsoft.CSharp.RuntimeBinder.Semantics.Scope

Malheureusement, je ne sais pas ce que c’est, alors il m’est un peu difficile de trouver comment / ce que je devrais publier.

J’ai vérifié l’arborescence des instances, mais cela va jusqu’au bout des choses Microsoft.

Le problème est que lorsque nous effectuons l ‘”ouverture / fermeture” d’un projet, nous parcourons beaucoup (la plupart de) notre code.

EDIT Une partie de notre application utilise le mot-clé dynamic pour certaines ressources, elle peut être liée. Les cours ici ne sont pas jetables, devrais-je faire quelque chose de spécial avec eux?

EDIT 2

Je suis presque sûr que cela est lié à mon travail dynamic , il semble que C # crée un cache lors de l’utilisation de Dynamic. Mais actuellement, je ne sais pas pourquoi cela se développe (je charge tout le temps les mêmes classes et j’aurai exactement la même signature tout le temps), ni comment effacer cela.

J’ai rencontré exactement le même problème aujourd’hui en profilant les memory leaks dans mon application RepoZ . Cet outil est censé s’exécuter en arrière-plan, en vérifiant les référentiels Git et en mettant à jour périodiquement les titres des fenêtres de l’Explorateur Windows. Cette dernière tâche doit faire des appels COM à “Shell.Application” pour rechercher les fenêtres de l’Explorateur et déterminer le chemin qu’elles pointent actuellement.

En utilisant le mot clé dynamic comme celui-ci …

 dynamic shell = Activator.CreateInstance(...); foreach (object window in shell.Windows()) { var hwnd = window.Hwnd; ... } 

… Je me suis retrouvé dans un tel dépotoir après quelques heures:

entrez la description de l'image ici

Combridge

Pour résoudre ce problème, j’ai écrit une petite classe d’assistance appelée “Combridge”, chargée de libérer les objects COM et offrant un access assez facile aux méthodes et propriétés de l’object COM sous-jacent. C’est très simple et facile, rien de spécial ici. Il utilise les objects Reflection to COM , c’est pourquoi il y a une perte de performance (voir ci-dessous).

L’exemple de code ci-dessus ressemble à ceci:

 using (var shell = new Combridge(Activator.CreateInstance(...))) { var windows = shell.InvokeMethod("Windows"); foreach (var window in windows) { var hwnd = window.GetPropertyValue("Hwnd"); ... } } 

Vous pouvez voir le fichier ExplorerWindowActor sur son utilisation dans RepoZ.

Ce n’est pas aussi beau qu’avec la dynamic et la performance s’est aussi dégradée lors de cette première tentative. Un banc rapide a montré ce qui suit:

Performance

J’ai testé 1000 itérations, à chaque itération, 10 fenêtres de l’explorateur ouvertes ont été traitées. Pour chaque fenêtre, 4 méthodes ou propriétés sont appelées sur cet object COM. Nous parlons donc de 40 000 appels COM.

La durée est passée de ~ 2500 ms ( dynamic ) à ~ 6000 ms ( Combridge ). Cela va de 0,062 ms à 0,150 ms pour chaque appel.

Donc, cela prend environ 2,4 fois le temps de terminer maintenant.

C’est important, je sais. Mais cela convient à mes besoins et la fuite de mémoire est partie.

C’est ça – je voulais partager cette histoire avec vous, j’espère que vous pourrez utiliser cette classe (ou une version améliorée de celle-ci) pour sortir également de cet enfer dynamic.

~ Mise à jour ~

Après 10 heures, RepoZ fonctionne toujours avec une empreinte mémoire très constante.

entrez la description de l'image ici

Ainsi, avec 10 fenêtres Explorer ouvertes, 4 appels COM par fenêtre et cette boucle entière deux fois par seconde, RepoZ a créé environ 72 000 instances COM et effectué environ 2 880 000 appels COM au total sans augmentation de la consommation de mémoire.

Je suppose que nous pouvons dire que le problème est vraiment dynamic .

Le mot-clé dynamic doit être rarement utilisé car, dans la plupart des cas, les solutions de contournement ne sont pas nécessaires.

Selon votre application, le meilleur conseil est de bien réfléchir à la possibilité de concevoir votre solution de manière à éviter toute dynamic. Voici quelques cas d’utilisation valables pour le traitement dynamic: https://msdn.microsoft.com/en-us/library/dd264736.aspx

Étant donné que vous devez vraiment utiliser dynamic, je suggérerais d’instruire votre code et de déterminer les parties qui consumnt le plus de mémoire. Effectivement, l’utilisation de Dynamic augmente votre consommation de mémoire, car elle nécessite d’effectuer toutes sortes de recherches . Toutefois , si vous avez une exception de mémoire insuffisante, vous devez utiliser un grand nombre de variables dynamics pour un grand nombre de types inconnus.

Il existe de nombreuses façons différentes d’appeler des méthodes sur des types inconnus, et il est essentiel de mesurer et d’ajuster les goulots d’étranglement.

PS: En outre, publier des extraits de code aide beaucoup.