Votre problème n'est pas dû à une fuite de mémoire gérée. Clairement, vous chatouillez un bug quelque part dans le code non géré.
La méthode SyncFlush() est appelée après plusieurs appels MILCore, et elle semble provoquer le traitement immédiat des modifications qui ont été envoyées au lieu d'être laissées dans la file d'attente pour un traitement ultérieur. Comme l'appel traite tout ce qui a été envoyé précédemment, rien dans votre arborescence visuelle ne peut être exclu de la pile d'appels que vous avez envoyée.
Une pile d'appels comprenant des appels non gérés peut générer des informations plus utiles. Exécutez l'application sous VS.NET avec un débogage natif ou avec windbg ou un autre débogueur de code natif. Définissez le débogueur pour interrompre l'exception et obtenez la pile d'appels au point d'arrêt relatif. La pile d'appels descendra bien sûr dans MILCore, et à partir de là, elle pourra aller dans la couche DirectX et le pilote DirectX. Un indice quant à la partie du code qui a causé le problème peut être trouvé quelque part dans cette pile d'appels native.
Les chances sont que MILCore passe une valeur énorme de certains paramètres dans DirectX en fonction de ce que vous lui dites. Vérifiez votre application pour tout ce qui pourrait causer un bogue qui permettrait à DirectX d'allouer beaucoup de mémoire. Exemples de choses à rechercher:
- BitmapSources définies pour le chargement à très haute résolution.
- Grand WritableBitmaps
- Très grande (ou négatif) transformer ou de la taille des valeurs
Une autre façon d'attaquer ce problème est de simplifier progressivement l'application jusqu'à ce que le problème disparaisse, regardez alors très closedly ce que vous avez supprimé la dernière . Lorsque cela est pratique, il peut être utile de faire cela comme une recherche binaire: d'abord couper la moitié de la complexité visuelle. Si cela fonctionne, remettre la moitié de ce qui a été retiré, sinon en retirer une autre moitié. Répétez jusqu'à ce que terminé.
Notez également qu'il n'est généralement pas nécessaire de supprimer les composants de l'interface utilisateur pour empêcher MILCore de les voir. Tout Visual avec Visibility.Hidden peut être entièrement ignoré.
Il n'existe pas de manière généralisée d'éviter ce problème, mais la technique de recherche vous aidera à identifier ce qui doit être modifié spécifiquement pour le corriger dans le cas d'espèce.
La pile d'appel indique que vous avez trouvé un bogue dans NET Framework ou dans les pilotes DirectX pour une carte vidéo particulière.
En ce qui concerne la deuxième trace de la pile que vous avez posté
John Knoeller est exact que la transition de RtlFreeHeap à ConvertToUnicode est un non-sens, mais tire la mauvaise conclusion. Ce que nous voyons, c'est que votre débogueur s'est perdu en retraçant la pile. Il a démarré correctement à partir de l'exception, mais s'est perdu en dessous du cadre Assembly.ExecuteMainMethod
car cette partie de la pile avait été remplacée car l'exception était gérée et le débogueur était appelé. Malheureusement, toute analyse de cette trace de pile est inutile pour vos besoins car elle a été capturée trop tard. Ce que nous voyons est une exception se produisant pendant le traitement d'un WM_LBUTTONDOWN qui est converti en WM_SYSCOMMAND, qui attrape alors une exception. En d'autres termes, vous avez cliqué sur quelque chose qui a causé une commande système (comme un redimensionnement), ce qui a provoqué une exception. Au moment où cette trace de pile a été capturée, l'exception était déjà gérée. La raison pour laquelle vous voyez des appels User32 et UxTheme est que ceux-ci sont impliqués dans le traitement du clic sur le bouton. Ils n'ont rien à voir avec le vrai problème.
Vous êtes sur la bonne voie, mais vous devrez capturer une trace de pile au moment où l'allocation échoue (ou vous pouvez utiliser l'une des autres approches que j'ai suggérées ci-dessus).
Vous saurez que vous disposez de la trace de pile correcte lorsque toutes les images managées dans votre première trace de pile s'affichent et que le haut de la pile est une allocation de mémoire défaillante. Notez que nous ne sommes intéressés que par les cadres non gérés qui apparaissent au-dessus de l'appel DUCE+Channel.SyncFlush
- tout ce qui sera au-dessous sera NET Framework et votre code d'application.
Comment obtenir une trace de la pile native au bon moment
Vous voulez obtenir une trace de pile au moment où le premier échec d'allocation de mémoire dans l'appel DUCE+Channel.SyncFlush
indiqué. Cela peut être difficile. Il y a trois approches que j'utilise: (notez que dans chaque cas, vous commencez avec un point d'arrêt dans l'appel de SyncFlush - voir note ci-dessous pour plus de détails)
Réglez le débogueur pour briser sur toutes les exceptions (gérées et non gérées) , puis continuez à aller (F5, ou "g") jusqu'à ce qu'il casse l'exception d'allocation de mémoire qui vous intéresse.C'est la première chose à essayer car elle est rapide, mais elle échoue souvent lorsque l'on travaille avec du code natif car le code natif renvoie souvent un code d'erreur au code natif appelant au lieu de lancer une exception. Définissez le débogueur pour interrompre toutes les exceptions et également définir des points d'arrêt sur les routines d'allocation de mémoire courantes, puis appuyez plusieurs fois sur F5 (go) jusqu'à ce que l'exception se produise, en comptant le nombre de F5 touchés. La prochaine fois que vous exécutez, utilisez un F5 de moins et vous pouvez être sur l'appel d'allocation qui a généré l'exception. Capturez la pile d'appels dans le Bloc-notes, puis F10 (pas à pas) à plusieurs reprises à partir de là pour voir si c'était vraiment l'allocation qui a échoué.
Définissez un point d'arrêt sur la première image native appelée par SyncFlush (c'est wpfgfx_v0300! MilComposition_SyncFlush) pour passer la transition gérée en native, puis F5 pour l'exécuter. F10 (étape par dessus) à travers la fonction jusqu'à ce que EAX contienne un des codes d'erreur E_OUTOFMEMORY (0x8007000E), ERROR_OUTOFMEMORY (0x0000000E), ou ERROR_NOT_ENOUGH_MEMORY (0x0000008). Notez l'instruction "Call" la plus récente. La prochaine fois que vous exécuterez le programme, courez vers là et entrez dedans. Répétez cette opération jusqu'à ce que vous soyez à l'appel d'allocation de mémoire qui a causé le problème et vider la trace de la pile. Notez que dans de nombreux cas, vous vous retrouvez à faire défiler une structure de données volumineuse. Il vous faut donc une certaine intelligence pour définir un point d'arrêt approprié afin de passer la boucle afin de pouvoir vous rendre rapidement. Cette technique est très fiable mais très laborieuse.
Remarque: Dans chaque cas, vous ne voulez pas définir des points d'arrêt ou commencer mode pas à pas jusqu'à ce que votre application est à l'intérieur du défaut DUCE+Channel.SyncFlush
appel. Pour cela, démarrez l'application avec tous les points d'arrêt désactivés. Lorsqu'il est en cours d'exécution, activez un point d'arrêt sur System.Windows.Media.Composition.DUCE+Channel.SyncFlush
et redimensionnez la fenêtre. La première fois, appuyez sur F5 pour vous assurer que l'exception échoue lors du premier appel SyncFlush (si ce n'est pas le cas, comptez combien de fois vous devez appuyer sur F5 avant que l'exception ne se produise). Ensuite, désactivez le point d'arrêt et redémarrez le programme. Répétez la procédure mais cette fois après avoir appuyé sur l'appel SyncFlush au bon moment, définissez vos points d'arrêt ou effectuez un seul pas comme décrit ci-dessus.
Recommandations
Les techniques de débogage ci-dessus sont que je décris en main-d'œuvre: Prévoyez de passer plusieurs heures au moins. Pour cette raison, j'essaie généralement de simplifier à plusieurs reprises mon application pour savoir exactement ce qui chatouille le bug avant de sauter dans le débogueur pour quelque chose comme ça. Cela a deux avantages: Il vous donnera une bonne repro pour envoyer le fournisseur de carte graphique, et il rendra votre débogage plus rapide car il y aura moins d'affichage et donc moins de code pour passer à travers, moins d'allocations, etc.
Parce que le problème se produit uniquement avec une carte graphique spécifique, il ne fait aucun doute que le problème est soit un bogue dans le pilote de la carte graphique ou dans le code MilCore qui l'appelle. Il est fort probable que ce soit dans le pilote de la carte graphique, mais il est possible que MilCore transmette des valeurs incorrectes qui sont gérées correctement par la plupart des cartes graphiques, mais pas par celle-ci. Les techniques de débogage que je décris ci-dessus vous diront que c'est le cas: Par exemple, si MilCore dit à la carte graphique d'allouer une zone 1000000x1000000 pixels et que la carte graphique donne des informations de résolution correctes, le bug est dans le MilCore. Mais si les demandes de MilCore sont raisonnables, alors le bug est dans le pilote de la carte graphique.
Merci pour votre aide et votre analyse. Merci Ray Burns, John Knoeller, Chris Nicol et tous les visiteurs. – whunmr
whunmr, avez-vous déjà été capable de trouver la cause du problème? Si oui, pourriez-vous partager vos résultats? – Charlie
Salut Charlie, je ne pense pas avoir trouvé la véritable cause de ce problème. et il y a longtemps. – whunmr