Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Cet article décrit les problèmes liés au garbage collection et à l’utilisation de la mémoire. Il résout les problèmes liés au tas managé et explique comment réduire l’effet du garbage collection sur vos applications. Chaque problème comporte des liens vers des procédures que vous pouvez utiliser pour examiner les problèmes.
Outils d’analyse des performances
Les sections suivantes décrivent les outils disponibles pour examiner les problèmes d’utilisation de la mémoire et de garbage collection. Les procédures fournies plus loin dans cet article font référence à ces outils.
Compteurs de performances de mémoire
Vous pouvez utiliser des compteurs de performances pour collecter des données de performances. Pour obtenir des instructions, consultez Profilage du runtime. La catégorie mémoire CLR .NET des compteurs de performances, comme décrit dans Les compteurs de performances dans .NET, fournit des informations sur le garbage collector.
Débogage avec SOS
Vous pouvez utiliser le débogueur Windows (WinDbg) pour inspecter des objets sur le tas managé.
Pour installer WinDbg, installez les outils de débogage pour Windows à partir de la page Télécharger les outils de débogage pour Windows .
Événements ETW de garbage collection
Le suivi d’événements pour Windows (ETW) est un système de suivi qui complète la prise en charge du profilage et du débogage fournie par .NET. À compter de .NET Framework 4, les événements ETW de garbage collection capturent des informations utiles pour analyser le tas managé d’un point de vue statistique. Par exemple, l’événement GCStart_V1 , déclenché lorsqu’un garbage collection est sur le point de se produire, fournit les informations suivantes :
- Quelle génération d’objets est collectée.
- Ce qui a déclenché le garbage collection.
- Type de garbage collection (simultané ou non simultané).
La journalisation des événements ETW est efficace et ne masque pas les problèmes de performances associés au garbage collection. Un processus peut fournir ses propres événements conjointement avec les événements ETW. Lors de la journalisation, les événements de l’application et les événements de garbage collection peuvent être corrélés pour déterminer comment et quand des problèmes de tas se produisent. Par exemple, une application serveur peut fournir des événements au début et à la fin d’une demande cliente.
L’API de profilage
Les interfaces de profilage CLR (Common Language Runtime) fournissent des informations détaillées sur les objets affectés pendant le garbage collection. Un profileur peut être averti lorsqu’un garbage collection démarre et se termine. Il peut fournir des rapports sur les objets sur le tas managé, y compris une identification d’objets dans chaque génération. Pour plus d’informations, consultez Vue d’ensemble du profilage.
Les profileurs peuvent fournir des informations complètes. Toutefois, les profils complexes peuvent éventuellement modifier le comportement d’une application.
Surveillance des ressources du domaine d’application
À compter de .NET Framework 4, la supervision des ressources de domaine d’application (ARM) permet aux hôtes de surveiller l’utilisation du processeur et de la mémoire par domaine d’application. Pour plus d’informations, consultez Surveillance des ressources du domaine d’application.
Résoudre les problèmes de performances
La première étape consiste à déterminer si le problème est réellement garbage collection. Si vous déterminez qu’il s’agit, sélectionnez-le dans la liste suivante pour résoudre le problème.
- Une exception hors mémoire est levée
- Le processus utilise trop de mémoire
- Le garbage collector ne récupère pas les objets assez rapidement
- Le tas managé est trop fragmenté
- Les pauses de garbage collection sont trop longues
- La génération 0 est trop grande
- L’utilisation du processeur pendant un garbage collection est trop élevée
Problème : une exception hors mémoire est levée
Il existe deux cas légitimes pour qu’une personne gérée OutOfMemoryException soit levée :
Manque de mémoire virtuelle.
Le garbage collector alloue de la mémoire du système dans des segments d’une taille prédéfinie. Si une allocation nécessite un segment supplémentaire, mais qu’il n’existe aucun bloc libre contigu gauche dans l’espace mémoire virtuelle du processus, l’allocation pour le tas managé échoue.
Pas suffisamment de mémoire physique pour allouer.
| Contrôle des performances |
|---|
|
Déterminez si l’exception hors mémoire est gérée. Déterminez la quantité de mémoire virtuelle pouvant être réservée. Déterminez s’il y a suffisamment de mémoire physique. |
Si vous déterminez que l’exception n’est pas légitime, contactez le service client microsoft et le support technique avec les informations suivantes :
- Pile avec l’exception de mémoire insuffisante managée.
- Vidage de mémoire complète.
- Les données qui prouvent qu’il n’est pas une exception de mémoire insuffisante légitime, y compris les données qui montrent que la mémoire virtuelle ou physique n’est pas un problème.
Problème : le processus utilise trop de mémoire
Une hypothèse courante est que l’affichage de l’utilisation de la mémoire sous l’onglet Performances du Gestionnaire des tâches Windows peut indiquer quand trop de mémoire est utilisée. Toutefois, cet affichage se rapporte au jeu de travail ; elle ne fournit pas d’informations sur l’utilisation de la mémoire virtuelle.
Si vous déterminez que le problème est dû au tas managé, vous devez mesurer le tas managé au fil du temps pour déterminer les modèles.
Si vous déterminez que le problème n’est pas dû au tas managé, vous devez utiliser le débogage natif.
Problème : le garbage collector ne récupère pas les objets assez rapidement
Lorsqu’il apparaît comme si les objets ne sont pas récupérés comme prévu pour le garbage collection, vous devez déterminer s’il existe des références fortes à ces objets.
Vous pouvez également rencontrer ce problème s’il n’y a pas eu de garbage collection pour la génération qui contient un objet mort, ce qui indique que le finaliseur de l’objet mort n’a pas été exécuté. Par exemple, cela est possible lorsque vous exécutez une application STA (Single-Threaded Apartment) et que le thread qui services la file d’attente du finaliseur ne peut pas l’appeler.
| Contrôle des performances |
|---|
|
Vérifiez les références aux objets. Déterminez si un finaliseur a été exécuté. Déterminez s’il existe des objets en attente d’être finalisés. |
Problème : le tas managé est trop fragmenté
Le niveau de fragmentation est calculé en tant que ratio d’espace libre sur la mémoire allouée totale pour la génération. Pour la génération 2, un niveau acceptable de fragmentation n’est pas supérieur à 20%. Étant donné que la génération 2 peut être très importante, le ratio de fragmentation est plus important que la valeur absolue.
Avoir beaucoup d’espace libre dans la génération 0 n’est pas un problème, car il s’agit de la génération où de nouveaux objets sont alloués.
La fragmentation se produit toujours dans le tas d’objets volumineux, car elle n’est pas compactée. Les objets libres adjacents sont naturellement réduits en un seul espace pour répondre aux demandes d’allocation d’objets volumineuses.
La fragmentation peut devenir un problème dans la génération 1 et la génération 2. Si ces générations ont une grande quantité d’espace libre après un garbage collection, l’utilisation de l’objet d’une application peut nécessiter une modification et vous devez envisager de réévaluer la durée de vie des objets à long terme.
L’épinglage excessif d’objets peut augmenter la fragmentation. Si la fragmentation est élevée, trop d’objets peuvent avoir été épinglés.
Si la fragmentation de la mémoire virtuelle empêche le garbage collector d’ajouter des segments, les causes peuvent être l’une des suivantes :
Chargement et déchargement fréquents de nombreux petits assemblys.
Conservation d’un trop grand nombre de références aux objets COM lors de l’interopérabilité avec du code non managé.
La création d’objets temporaires volumineux, ce qui entraîne l’allocation et la libération des segments de tas d’objets volumineux.
Lors de l’hébergement du CLR, une application peut demander que le garbage collector conserve ses segments. Cela réduit la fréquence des allocations de segments. Pour ce faire, utilisez l’indicateur STARTUP_HOARD_GC_VM dans l’énumération STARTUP_FLAGS.
| Contrôle des performances |
|---|
|
Déterminez la quantité d’espace libre dans le tas managé. Déterminez le nombre d’objets épinglés. |
Si vous pensez qu’il n’existe aucune cause légitime pour la fragmentation, contactez le service clientèle et le support technique Microsoft.
Problème : les pauses de garbage collection sont trop longues
Le garbage collection fonctionne en temps réel, de sorte qu’une application doit être en mesure de tolérer certaines pauses. Un critère pour le temps réel doux est que 95% des opérations doivent se terminer à temps.
Dans le garbage collection simultané, les threads managés sont autorisés à s’exécuter pendant un regroupement, ce qui signifie que les pauses sont très minimes.
Les garbage collections éphémères (générations 0 et 1) ne durent que quelques millisecondes, de sorte que la diminution des pauses n’est généralement pas réalisable. Toutefois, vous pouvez réduire les pauses dans les collections de génération 2 en modifiant le modèle de demandes d’allocation par une application.
Une autre méthode plus précise consiste à utiliser des événements ETW de garbage collection. Vous pouvez trouver les minutages des regroupements en ajoutant les différences d’horodatage pour une séquence d’événements. L’ensemble de la séquence de collecte comprend la suspension du moteur d’exécution, le garbage collection lui-même et la reprise du moteur d’exécution.
Vous pouvez utiliser les notifications de garbage collection pour déterminer si un serveur est sur le point d’avoir un regroupement de génération 2 et si la réacheminement des demandes vers un autre serveur peut faciliter les problèmes liés aux pauses.
| Contrôle des performances |
|---|
|
Déterminez la durée d’un garbage collection. Déterminez ce qui a provoqué un garbage collection. |
Problème : la génération 0 est trop importante
La génération 0 est susceptible d’avoir un plus grand nombre d’objets sur un système 64 bits, en particulier lorsque vous utilisez le garbage collection serveur au lieu de garbage collection de stations de travail. Cela est dû au fait que le seuil de déclenchement d’un garbage collection de génération 0 est plus élevé dans ces environnements, et les regroupements de génération 0 peuvent être beaucoup plus volumineux. Les performances sont améliorées lorsqu’une application alloue plus de mémoire avant le déclenchement d’un garbage collection.
Problème : l’utilisation du processeur pendant un garbage collection est trop élevée
L’utilisation du processeur est élevée pendant un garbage collection. Si un temps de traitement important est passé dans un garbage collection, le nombre de regroupements est trop fréquent ou le regroupement dure trop longtemps. Un taux d’allocation accru d’objets sur le tas managé entraîne le garbage collection plus fréquemment. La diminution du taux d’allocation réduit la fréquence des nettoyages de mémoire.
Vous pouvez surveiller les taux d’allocation à l’aide du compteur de Allocated Bytes/second performances. Pour plus d’informations, consultez Compteurs de performances dans .NET.
La durée d’une collection est principalement un facteur du nombre d’objets qui survivent après l’allocation. Le garbage collector doit passer par une grande quantité de mémoire si de nombreux objets restent à collecter. Le travail de compactage des survivants prend beaucoup de temps. Pour déterminer le nombre d’objets gérés pendant une collection, définissez un point d’arrêt dans le débogueur à la fin d’un garbage collection pour une génération spécifiée.
| Contrôle des performances |
|---|
|
Déterminez si une utilisation élevée du processeur est due au garbage collection. Définissez un point d’arrêt à la fin du garbage collection. |
Instructions de dépannage
Cette section décrit les instructions que vous devez prendre en compte lorsque vous commencez vos enquêtes.
Garbage collection de stations de travail ou serveur
Déterminez si vous utilisez le type correct de garbage collection. Si votre application utilise plusieurs threads et instances d’objet, utilisez le garbage collection de serveurs plutôt que le garbage collection de stations de travail. Le garbage collection de serveur fonctionne sur plusieurs threads, tandis que le garbage collection de stations de travail nécessite plusieurs instances d’une application pour exécuter leurs propres threads de garbage collection et concurrencer le temps processeur.
Une application qui a une charge faible et qui effectue des tâches rarement en arrière-plan, comme un service, peut utiliser le garbage collection de stations de travail avec le garbage collection simultané désactivé.
Quand mesurer la taille du tas managé
Sauf si vous utilisez un profileur, vous devez établir un modèle de mesure cohérent pour diagnostiquer efficacement les problèmes de performances. Tenez compte des points suivants pour établir une planification :
- Si vous mesurez après un garbage collection de génération 2, l’intégralité du tas managé sera libre de garbage (objets morts).
- Si vous mesurez immédiatement après un garbage collection de génération 0, les objets des générations 1 et 2 ne seront pas encore collectés.
- Si vous mesurez immédiatement avant un garbage collection, vous mesurez autant d’allocation que possible avant le démarrage du garbage collection.
- La mesure pendant un garbage collection est problématique, car les structures de données du garbage collector ne sont pas dans un état valide pour la traversée et peuvent ne pas être en mesure de vous donner les résultats complets. C'est intentionnel.
- Lorsque vous utilisez le garbage collection de station de travail avec le garbage collection simultané, les objets récupérés ne sont pas compactés, de sorte que la taille du tas peut être identique ou plus grande (la fragmentation peut le rendre plus grande).
- Le garbage collection simultané de la génération 2 est retardé lorsque la charge de mémoire physique est trop élevée.
La procédure suivante explique comment définir un point d’arrêt afin de pouvoir mesurer le tas managé.
Pour définir un point d’arrêt à la fin du garbage collection
Dans WinDbg avec l’extension du débogueur SOS chargée, entrez la commande suivante :
bp mscorwks!WKS::GCHeap::RestartEE "j (dwo(mscorwks!WKS::GCHeap::GcCondemnedGeneration)==2) 'kb';'g'"Définissez
GcCondemnedGenerationla génération souhaitée. Cette commande nécessite des symboles privés.Cette commande force un arrêt s’il
RestartEEest exécuté après la récupération des objets de génération 2 pour le garbage collection.Dans le garbage collection de serveurs, un seul appel de thread
RestartEEne se produit donc qu’une seule fois lors d’un garbage collection de génération 2.
Procédures de vérification des performances
Cette section décrit les procédures suivantes pour isoler la cause de votre problème de performances :
- Déterminez si le problème est dû au garbage collection.
- Déterminez si l’exception hors mémoire est gérée.
- Déterminez la quantité de mémoire virtuelle pouvant être réservée.
- Déterminez s’il y a suffisamment de mémoire physique.
- Déterminez la quantité de mémoire que le tas managé valide.
- Déterminez la quantité de mémoire que le tas managé réserve.
- Déterminez les objets volumineux de la génération 2.
- Déterminez les références aux objets.
- Déterminez si un finaliseur a été exécuté.
- Déterminez s’il existe des objets en attente d’être finalisés.
- Déterminez la quantité d’espace libre dans le tas managé.
- Déterminez le nombre d’objets épinglés.
- Déterminez la durée d’un garbage collection.
- Déterminez ce qui a déclenché un garbage collection.
- Déterminez si l’utilisation élevée du processeur est due au garbage collection.
Pour déterminer si le problème est dû au garbage collection
Examinez les deux compteurs de performances de mémoire suivants :
% heure dans GC. Affiche le pourcentage de temps écoulé passé à effectuer un garbage collection après le dernier cycle de garbage collection. Utilisez ce compteur pour déterminer si le garbage collector passe trop de temps pour rendre l’espace de tas managé disponible. Si le temps passé dans le garbage collection est relativement faible, cela peut indiquer un problème de ressource en dehors du tas managé. Ce compteur peut ne pas être précis lorsque le garbage collection simultané ou en arrière-plan est impliqué.
Nombre total d’octets validés. Affiche la quantité de mémoire virtuelle actuellement validée par le garbage collector. Utilisez ce compteur pour déterminer si la mémoire consommée par le garbage collector est une partie excessive de la mémoire utilisée par votre application.
La plupart des compteurs de performances de mémoire sont mis à jour à la fin de chaque garbage collection. Par conséquent, ils peuvent ne pas refléter les conditions actuelles sur lesquelles vous souhaitez obtenir des informations.
Pour déterminer si l’exception hors mémoire est gérée
Dans le débogueur WinDbg ou Visual Studio avec l’extension de débogueur SOS chargée, entrez la commande d’exception d’impression (
pe) :!peSi l’exception est gérée, OutOfMemoryException s’affiche en tant que type d’exception, comme illustré dans l’exemple suivant.
Exception object: 39594518 Exception type: System.OutOfMemoryException Message: <none> InnerException: <none> StackTrace (generated):Si la sortie ne spécifie pas d’exception, vous devez déterminer le thread à partir duquel l’exception hors mémoire provient. Entrez la commande suivante dans le débogueur pour afficher tous les threads avec leurs piles d’appels :
~\*kbLe thread avec la pile qui a des appels d’exception est indiqué par l’argument
RaiseTheException. Il s’agit de l’objet d’exception managé.28adfb44 7923918f 5b61f2b4 00000000 5b61f2b4 mscorwks!RaiseTheException+0xa0Vous pouvez utiliser la commande suivante pour vider les exceptions imbriquées.
!pe -nestedSi vous ne trouvez aucune exception, l’exception hors mémoire provient du code non managé.
Pour déterminer la quantité de mémoire virtuelle pouvant être réservée
Dans WinDbg avec l’extension de débogueur SOS chargée, entrez la commande suivante pour obtenir la plus grande région gratuite :
!address -summaryLa plus grande région libre est affichée comme indiqué dans la sortie suivante.
Largest free region: Base 54000000 - Size 0003A980Dans cet exemple, la taille de la plus grande région libre est d’environ 24 000 Ko (3A980 en hexadécimal). Cette région est beaucoup plus petite que ce dont le garbage collector a besoin pour un segment.
-ou-
Utilisez la commande
vmstat:!vmstatLa plus grande région libre est la plus grande valeur de la colonne MAXIMUM, comme indiqué dans la sortie suivante.
TYPE MINIMUM MAXIMUM AVERAGE BLK COUNT TOTAL ~~~~ ~~~~~~~ ~~~~~~~ ~~~~~~~ ~~~~~~~~~~ ~~~~ Free: Small 8K 64K 46K 36 1,671K Medium 80K 864K 349K 3 1,047K Large 1,384K 1,278,848K 151,834K 12 1,822,015K Summary 8K 1,278,848K 35,779K 51 1,824,735K
Pour déterminer s’il y a suffisamment de mémoire physique
Démarrez le Gestionnaire des tâches Windows.
Sous l’onglet
Performance, examinez la valeur validée. (Dans Windows 7, regardezCommit (KB)dans leSystem groupfichier .)Si l’élément
Totalest proche de celui-ci, vous êtes en cours d’exécution faible sur laLimitmémoire physique.
Pour déterminer la quantité de mémoire que le tas managé valide
Utilisez le
# Total committed bytescompteur de performances de mémoire pour obtenir le nombre d’octets validés par le tas managé. Le garbage collector valide les blocs sur un segment en fonction des besoins, pas tous en même temps.Remarque
N’utilisez pas le
# Bytes in all Heapscompteur de performances, car il ne représente pas l’utilisation réelle de la mémoire par le tas managé. La taille d’une génération est incluse dans cette valeur et est en fait sa taille de seuil, c’est-à-dire la taille qui induise un garbage collection si la génération est remplie d’objets. Par conséquent, cette valeur est généralement égale à zéro.
Pour déterminer la quantité de mémoire que le tas managé réserve
Utilisez le compteur de performances de
# Total reserved bytesmémoire.Le garbage collector réserve de la mémoire dans les segments, et vous pouvez déterminer où un segment commence à l’aide de la
eeheapcommande.Important
Bien que vous puissiez déterminer la quantité de mémoire allouée par le garbage collector pour chaque segment, la taille du segment est spécifique à l’implémentation et peut être modifiée à tout moment, y compris dans les mises à jour périodiques. Votre application ne doit jamais faire d'hypothèses concernant une taille de segment particulière, ni dépendre de celle-ci. Elle ne doit pas non plus tenter de configurer la quantité de mémoire disponible pour les allocations de segments.
Dans le débogueur WinDbg ou Visual Studio avec l’extension de débogueur SOS chargée, entrez la commande suivante :
!eeheap -gcLe résultat est le suivant.
Number of GC Heaps: 2 ------------------------------ Heap 0 (002db550) generation 0 starts at 0x02abe29c generation 1 starts at 0x02abdd08 generation 2 starts at 0x02ab0038 ephemeral segment allocation context: none segment begin allocated size 02ab0000 02ab0038 02aceff4 0x0001efbc(126908) Large object heap starts at 0x0aab0038 segment begin allocated size 0aab0000 0aab0038 0aab2278 0x00002240(8768) Heap Size 0x211fc(135676) ------------------------------ Heap 1 (002dc958) generation 0 starts at 0x06ab1bd8 generation 1 starts at 0x06ab1bcc generation 2 starts at 0x06ab0038 ephemeral segment allocation context: none segment begin allocated size 06ab0000 06ab0038 06ab3be4 0x00003bac(15276) Large object heap starts at 0x0cab0038 segment begin allocated size 0cab0000 0cab0038 0cab0048 0x00000010(16) Heap Size 0x3bbc(15292) ------------------------------ GC Heap Size 0x24db8(150968)Les adresses indiquées par « segment » sont les adresses de départ des segments.
Pour déterminer les objets volumineux de la génération 2
Dans le débogueur WinDbg ou Visual Studio avec l’extension de débogueur SOS chargée, entrez la commande suivante :
!dumpheap –statSi le tas managé est volumineux,
dumpheappeut prendre un certain temps pour terminer.Vous pouvez commencer à analyser à partir des dernières lignes de la sortie, car ils répertorient les objets qui utilisent le plus d’espace. Par exemple:
2c6108d4 173712 14591808 DevExpress.XtraGrid.Views.Grid.ViewInfo.GridCellInfo 00155f80 533 15216804 Free 7a747c78 791070 15821400 System.Collections.Specialized.ListDictionary+DictionaryNode 7a747bac 700930 19626040 System.Collections.Specialized.ListDictionary 2c64e36c 78644 20762016 DevExpress.XtraEditors.ViewInfo.TextEditViewInfo 79124228 121143 29064120 System.Object[] 035f0ee4 81626 35588936 Toolkit.TlkOrder 00fcae40 6193 44911636 WaveBasedStrategy.Tick_Snap[] 791242ec 40182 90664128 System.Collections.Hashtable+bucket[] 790fa3e0 3154024 137881448 System.String Total 8454945 objectsLe dernier objet répertorié est une chaîne et occupe le plus d’espace. Vous pouvez examiner votre application pour voir comment vos objets de chaîne peuvent être optimisés. Pour afficher les chaînes comprises entre 150 et 200 octets, entrez les éléments suivants :
!dumpheap -type System.String -min 150 -max 200Voici un exemple de résultats.
Address MT Size Gen 1875d2c0 790fa3e0 152 2 System.String HighlightNullStyle_Blotter_PendingOrder-11_Blotter_PendingOrder-11 …L’utilisation d’un entier au lieu d’une chaîne pour un ID peut être plus efficace. Si la même chaîne est répétée des milliers de fois, envisagez de faire un interne de chaîne. Pour plus d’informations sur l’interne de chaînes, consultez la rubrique de référence pour la String.Intern méthode.
Pour déterminer les références aux objets
Dans WinDbg avec l’extension de débogueur SOS chargée, entrez la commande suivante pour répertorier les références aux objets :
!gcroot-ou-
Pour déterminer les références d’un objet spécifique, incluez l’adresse :
!gcroot 1c37b2acLes racines trouvées sur les piles peuvent être des faux positifs. Pour plus d'informations, utilisez la commande
!help gcroot.ebx:Root:19011c5c(System.Windows.Forms.Application+ThreadContext)-> 19010b78(DemoApp.FormDemoApp)-> 19011158(System.Windows.Forms.PropertyStore)-> … [omitted] 1c3745ec(System.Data.DataTable)-> 1c3747a8(System.Data.DataColumnCollection)-> 1c3747f8(System.Collections.Hashtable)-> 1c376590(System.Collections.Hashtable+bucket[])-> 1c376c98(System.Data.DataColumn)-> 1c37b270(System.Data.Common.DoubleStorage)-> 1c37b2ac(System.Double[]) Scan Thread 0 OSTHread 99c Scan Thread 6 OSTHread 484La
gcrootcommande peut prendre beaucoup de temps. Tout objet qui n’est pas récupéré par le garbage collection est un objet actif. Cela signifie qu’une racine est directement ou indirectement conservée sur l’objet. Elle doit doncgcrootretourner les informations de chemin d’accès à l’objet. Vous devez examiner les graphiques retournés et voir pourquoi ces objets sont toujours référencés.
Pour déterminer si un finaliseur a été exécuté
Exécutez un programme de test qui contient le code suivant :
GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect();Si le test résout le problème, cela signifie que le garbage collector n’a pas récupéré d’objets, car les finaliseurs de ces objets ont été suspendus. La GC.WaitForPendingFinalizers méthode permet aux finaliseurs d’effectuer leurs tâches et de résoudre le problème.
Pour déterminer s’il existe des objets en attente d’être finalisés
Dans le débogueur WinDbg ou Visual Studio avec l’extension de débogueur SOS chargée, entrez la commande suivante :
!finalizequeueExaminez le nombre d’objets prêts à être finalisés. Si le nombre est élevé, vous devez examiner pourquoi ces finaliseurs ne peuvent pas progresser du tout ou ne peuvent pas progresser suffisamment rapidement.
Pour obtenir une sortie de threads, entrez la commande suivante :
!threads -specialCette commande fournit une sortie telle que la suivante.
OSID Special thread type 2 cd0 DbgHelper 3 c18 Finalizer 4 df0 GC SuspendEELe thread de finaliseur indique quel finaliseur, le cas échéant, est en cours d’exécution. Lorsqu’un thread de finaliseur n’exécute pas de finaliseurs, il attend qu’un événement lui indique de faire son travail. La plupart du temps, vous verrez le thread de finaliseur dans cet état, car il s’exécute à THREAD_HIGHEST_PRIORITY et est censé terminer les finaliseurs en cours d’exécution, le cas échéant, très rapidement.
Pour déterminer la quantité d’espace libre dans le tas managé
Dans le débogueur WinDbg ou Visual Studio avec l’extension de débogueur SOS chargée, entrez la commande suivante :
!dumpheap -type Free -statCette commande affiche la taille totale de tous les objets libres sur le tas managé, comme illustré dans l’exemple suivant.
total 230 objects Statistics: MT Count TotalSize Class Name 00152b18 230 40958584 Free Total 230 objectsPour déterminer l’espace libre de la génération 0, entrez la commande suivante pour les informations de consommation de mémoire par génération :
!eeheap -gcCette commande affiche une sortie similaire à ce qui suit. La dernière ligne affiche le segment éphémère.
Heap 0 (0015ad08) generation 0 starts at 0x49521f8c generation 1 starts at 0x494d7f64 generation 2 starts at 0x007f0038 ephemeral segment allocation context: none segment begin allocated size 00178250 7a80d84c 7a82f1cc 0x00021980(137600) 00161918 78c50e40 78c7056c 0x0001f72c(128812) 007f0000 007f0038 047eed28 0x03ffecf0(67103984) 3a120000 3a120038 3a3e84f8 0x002c84c0(2917568) 46120000 46120038 49e05d04 0x03ce5ccc(63855820)Calculez l’espace utilisé par la génération 0 :
? 49e05d04-0x49521f8cLe résultat est le suivant. La génération 0 est d’environ 9 Mo.
Evaluate expression: 9321848 = 008e3d78La commande suivante vide l’espace libre dans la plage de génération 0 :
!dumpheap -type Free -stat 0x49521f8c 49e05d04Le résultat est le suivant.
------------------------------ Heap 0 total 409 objects ------------------------------ Heap 1 total 0 objects ------------------------------ Heap 2 total 0 objects ------------------------------ Heap 3 total 0 objects ------------------------------ total 409 objects Statistics: MT Count TotalSize Class Name 0015a498 409 7296540 Free Total 409 objectsCette sortie montre que la partie de génération 0 du tas utilise 9 Mo d’espace pour les objets et a 7 Mo libres. Cette analyse montre la mesure dans laquelle la génération 0 contribue à la fragmentation. Cette quantité d’utilisation du tas doit être remise du montant total comme cause de fragmentation par les objets à long terme.
Pour déterminer le nombre d’objets épinglés
Dans le débogueur WinDbg ou Visual Studio avec l’extension de débogueur SOS chargée, entrez la commande suivante :
!gchandlesLes statistiques affichées incluent le nombre de handles épinglés, comme l’illustre l’exemple suivant.
GC Handle Statistics: Strong Handles: 29 Pinned Handles: 10
Pour déterminer la durée d’un garbage collection
Examinez le compteur de performances de
% Time in GCmémoire.La valeur est calculée à l’aide d’un exemple de temps d’intervalle. Étant donné que les compteurs sont mis à jour à la fin de chaque garbage collection, l’exemple actuel aura la même valeur que l’exemple précédent si aucun regroupement n’a eu lieu pendant l’intervalle.
Le temps de collecte est obtenu en multipliant l’intervalle d’échantillonnage par la valeur de pourcentage.
Les données suivantes montrent quatre intervalles d’échantillonnage de deux secondes, pour une étude de 8 secondes. Les
Gen0colonnes etGen2les colonnesGen1indiquent le nombre total de garbage collections terminées à la fin de l’intervalle pour cette génération.Interval Gen0 Gen1 Gen2 % Time in GC 1 9 3 1 10 2 10 3 1 1 3 11 3 1 3 4 11 3 1 3Ces informations ne s’affichent pas lorsque le garbage collection s’est produit, mais vous pouvez déterminer le nombre de garbage collections qui se sont produits dans un intervalle de temps. En supposant le pire cas, le dixième garbage collection de génération 0 a terminé au début de la deuxième intervalle, et le onzee génération 0 garbage collection a terminé à la fin du troisième intervalle. La durée entre la fin du dixième et la fin du 1ième garbage collection est d’environ 2 secondes, et le compteur de performances affiche 3%, de sorte que la durée du 1ième garbage collection de génération 0 était (2 secondes * 3% = 60 ms).
Dans l’exemple suivant, il existe cinq intervalles.
Interval Gen0 Gen1 Gen2 % Time in GC 1 9 3 1 3 2 10 3 1 1 3 11 4 1 1 4 11 4 1 1 5 11 4 2 20Le deuxième garbage collection de génération 2 a commencé pendant le quatrième intervalle et s’est terminé à la cinquième intervalle. En supposant le pire cas, le dernier garbage collection était pour un regroupement de génération 0 qui s’est terminé au début de la troisième période, et le garbage collection de génération 2 a terminé à la fin de la cinquième intervalle. Par conséquent, le temps entre la fin du garbage collection de génération 0 et la fin du garbage collection de génération 2 est de 4 secondes. Étant donné que le
% Time in GCcompteur est de 20%, la durée maximale pendant laquelle le garbage collection de génération 2 aurait pu être effectué est (4 secondes * 20% = 800 ms).Vous pouvez également déterminer la longueur d’un garbage collection à l’aide d’événements ETW de garbage collection et analyser les informations pour déterminer la durée du garbage collection.
Par exemple, les données suivantes montrent une séquence d’événements qui s’est produite pendant un garbage collection non simultané.
Timestamp Event name 513052 GCSuspendEEBegin_V1 513078 GCSuspendEEEnd 513090 GCStart_V1 517890 GCEnd_V1 517894 GCHeapStats 517897 GCRestartEEBegin 517918 GCRestartEEEndLa suspension du thread managé a pris 26us ( –
GCSuspendEEEndGCSuspendEEBegin_V1).Le garbage collection réel a pris 4,8 ms ( –
GCEnd_V1GCStart_V1).La reprise des threads managés a pris 21us ( –
GCRestartEEEndGCRestartEEBegin).La sortie suivante fournit un exemple de garbage collection d’arrière-plan et inclut les champs processus, thread et événement. (Toutes les données ne sont pas affichées.)
timestamp(us) event name process thread event field 42504385 GCSuspendEEBegin_V1 Test.exe 4372 1 42504648 GCSuspendEEEnd Test.exe 4372 42504816 GCStart_V1 Test.exe 4372 102019 42504907 GCStart_V1 Test.exe 4372 102020 42514170 GCEnd_V1 Test.exe 4372 42514204 GCHeapStats Test.exe 4372 102020 42832052 GCRestartEEBegin Test.exe 4372 42832136 GCRestartEEEnd Test.exe 4372 63685394 GCSuspendEEBegin_V1 Test.exe 4744 6 63686347 GCSuspendEEEnd Test.exe 4744 63784294 GCRestartEEBegin Test.exe 4744 63784407 GCRestartEEEnd Test.exe 4744 89931423 GCEnd_V1 Test.exe 4372 102019 89931464 GCHeapStats Test.exe 4372L’événement
GCStart_V1à 42504816 indique qu’il s’agit d’un garbage collection d’arrière-plan, car le dernier champ est1. Cela devient garbage collection No. 102019.L’événement
GCStartse produit parce qu’il est nécessaire de disposer d’un garbage collection éphémère avant de démarrer un garbage collection d’arrière-plan. Cela devient garbage collection No. 102020.À 42514170, le garbage collection n° 102020 se termine. Les threads managés sont redémarrés à ce stade. Cette opération est terminée sur le thread 4372, ce qui a déclenché ce garbage collection d’arrière-plan.
Sur le thread 4744, une suspension se produit. Il s’agit du seul moment auquel le garbage collection d’arrière-plan doit suspendre les threads managés. Cette durée est d’environ 99 ms ((63784407-63685394)/1000).
L’événement
GCEnddu garbage collection d’arrière-plan se trouve à 89931423. Cela signifie que le garbage collection d’arrière-plan a duré environ 47secondes ((89931423-42504816)/1000).Pendant que les threads managés sont en cours d’exécution, vous pouvez voir n’importe quel nombre de garbage collections éphémères se produisant.
Pour déterminer ce qui a déclenché un garbage collection
Dans le débogueur WinDbg ou Visual Studio avec l’extension de débogueur SOS chargée, entrez la commande suivante pour afficher tous les threads avec leurs piles d’appels :
~*Ko
Cette commande affiche une sortie similaire à ce qui suit.
0012f3b0 79ff0bf8 mscorwks!WKS::GCHeap::GarbageCollect 0012f454 30002894 mscorwks!GCInterface::CollectGeneration+0xa4 0012f490 79fa22bd fragment_ni!request.Main(System.String[])+0x48Si le garbage collection a été provoqué par une notification de mémoire faible du système d’exploitation, la pile des appels est similaire, sauf que le thread est le thread finaliseur. Le thread finaliseur obtient une notification asynchrone à faible mémoire et induise le garbage collection.
Si le garbage collection a été provoqué par l’allocation de mémoire, la pile s’affiche comme suit :
0012f230 7a07c551 mscorwks!WKS::GCHeap::GarbageCollectGeneration 0012f2b8 7a07cba8 mscorwks!WKS::gc_heap::try_allocate_more_space+0x1a1 0012f2d4 7a07cefb mscorwks!WKS::gc_heap::allocate_more_space+0x18 0012f2f4 7a02a51b mscorwks!WKS::GCHeap::Alloc+0x4b 0012f310 7a02ae4c mscorwks!Alloc+0x60 0012f364 7a030e46 mscorwks!FastAllocatePrimitiveArray+0xbd 0012f424 300027f4 mscorwks!JIT_NewArr1+0x148 000af70f 3000299f fragment_ni!request..ctor(Int32, Single)+0x20c 0000002a 79fa22bd fragment_ni!request.Main(System.String[])+0x153Un assistance juste-à-temps (
JIT_New*) appelleGCHeap::GarbageCollectGenerationfinalement . Si vous déterminez que les garbage collections de génération 2 sont provoqués par des allocations, vous devez déterminer quels objets sont collectés par un garbage collection de génération 2 et comment les éviter. Autrement dit, vous souhaitez déterminer la différence entre le début et la fin d’un garbage collection de génération 2 et les objets qui ont provoqué la collecte de génération 2.Par exemple, entrez la commande suivante dans le débogueur pour afficher le début d’une collection de génération 2 :
!dumpheap –statExemple de sortie (abrégé pour afficher les objets qui utilisent le plus d’espace) :
79124228 31857 9862328 System.Object[] 035f0384 25668 11601936 Toolkit.TlkPosition 00155f80 21248 12256296 Free 79103b6c 297003 13068132 System.Threading.ReaderWriterLock 7a747ad4 708732 14174640 System.Collections.Specialized.HybridDictionary 7a747c78 786498 15729960 System.Collections.Specialized.ListDictionary+DictionaryNode 7a747bac 700298 19608344 System.Collections.Specialized.ListDictionary 035f0ee4 89192 38887712 Toolkit.TlkOrder 00fcae40 6193 44911636 WaveBasedStrategy.Tick_Snap[] 7912c444 91616 71887080 System.Double[] 791242ec 32451 82462728 System.Collections.Hashtable+bucket[] 790fa3e0 2459154 112128436 System.String Total 6471774 objectsRépétez la commande à la fin de la génération 2 :
!dumpheap –statExemple de sortie (abrégé pour afficher les objets qui utilisent le plus d’espace) :
79124228 26648 9314256 System.Object[] 035f0384 25668 11601936 Toolkit.TlkPosition 79103b6c 296770 13057880 System.Threading.ReaderWriterLock 7a747ad4 708730 14174600 System.Collections.Specialized.HybridDictionary 7a747c78 786497 15729940 System.Collections.Specialized.ListDictionary+DictionaryNode 7a747bac 700298 19608344 System.Collections.Specialized.ListDictionary 00155f80 13806 34007212 Free 035f0ee4 89187 38885532 Toolkit.TlkOrder 00fcae40 6193 44911636 WaveBasedStrategy.Tick_Snap[] 791242ec 32370 82359768 System.Collections.Hashtable+bucket[] 790fa3e0 2440020 111341808 System.String Total 6417525 objectsLes
double[]objets ont disparu de la fin de la sortie, ce qui signifie qu’ils ont été collectés. Ces objets représentent environ 70 Mo. Les objets restants n’ont pas beaucoup changé. Par conséquent, cesdouble[]objets ont été la raison pour laquelle ce garbage collection de génération 2 s’est produit. Votre prochaine étape consiste à déterminer pourquoi lesdouble[]objets sont là et pourquoi ils sont morts. Vous pouvez demander au développeur de code d’où proviennent ces objets, ou vous pouvez utiliser lagcrootcommande.
Pour déterminer si une utilisation élevée du processeur est due au garbage collection
Mettre en corrélation la valeur du
% Time in GCcompteur de performances de mémoire avec l’heure du processus.Si la
% Time in GCvaleur pointe en même temps que le temps du processus, le garbage collection provoque une utilisation élevée du processeur. Sinon, profilez l’application pour trouver où se produit l’utilisation élevée.