109 votes

Qu'y a-t-il de si mal à utiliser GC.Collect() ?

Bien que je comprenne les implications sérieuses de jouer avec cette fonction (ou du moins c'est ce que je pense), je ne vois pas pourquoi elle devient une de ces choses que les programmeurs respectables n'utiliseraient jamais, même ceux qui ne savent même pas à quoi elle sert.

Imaginons que je développe une application où l'utilisation de la mémoire varie énormément en fonction de ce que fait l'utilisateur. Le cycle de vie de l'application peut être divisé en deux étapes principales : l'édition et le traitement en temps réel. Au cours de l'étape d'édition, supposons que des milliards, voire des trillions d'objets sont créés ; certains sont petits et d'autres non, certains peuvent avoir des finaliseurs et d'autres non, et supposons que leur durée de vie varie de quelques millisecondes à de longues heures. Ensuite, l'utilisateur décide de passer à l'étape du temps réel. A ce stade, supposons que les performances jouent un rôle fondamental et que la moindre altération du déroulement du programme pourrait avoir des conséquences catastrophiques. La création d'objets est alors réduite au minimum grâce à l'utilisation de pools d'objets et autres, mais le GC intervient inopinément et jette tout, et quelqu'un meurt.

La question : Dans ce cas, ne serait-il pas judicieux d'appeler GC.Collect() avant d'entrer dans la deuxième étape ?

Après tout, ces deux étapes ne se chevauchent jamais dans le temps l'une par rapport à l'autre et toute l'optimisation et les statistiques que le GC aurait pu recueillir ne seraient pas d'une grande utilité ici...

Remarque : comme certains d'entre vous l'ont souligné, .NET n'est peut-être pas la meilleure plateforme pour une application de ce type, mais cela dépasse le cadre de cette question. L'intention est de clarifier si un appel à GC.Collect() peut améliorer le comportement/les performances globales d'une application ou non. Nous sommes tous d'accord pour dire que les circonstances dans lesquelles vous feriez une telle chose sont extrêmement rares, mais là encore, le GC essaie de deviner et le fait parfaitement bien la plupart du temps, mais il s'agit toujours de deviner.

Gracias.

25 votes

"La moindre altération du déroulement du programme pourrait avoir des conséquences catastrophiques... quelqu'un pourrait mourir" - êtes-vous sûr que C# .NET est suffisamment déterministe pour vos besoins ?

4 votes

Ni Windows ni .NET ne sont des plateformes en temps réel et vous ne pouvez donc pas garantir les mesures de performance, du moins pas au point de risquer des vies humaines. Je suis d'accord avec onebyone pour dire que vous exagérez ou que vous êtes négligent.

3 votes

LOL à "une de ces choses que les programmeurs respectables n'utiliseraient jamais, même ceux qui ne savent même pas à quoi ça sert" ! Les programmeurs qui utilisent des choses sans savoir pourquoi ne sont pas les plus respectables dans mon livre :)

90voto

Jon Norton Points 2048

Du blog de Rico...

Règle n° 1

Ne le faites pas.

C'est vraiment le plus important règle la plus importante. Il est juste de dire que la plupart utilisations de GC.Collect() sont une mauvaise idée. et je l'ai expliqué en détail dans le l'article original, je ne vais donc pas répéter tout cela tout cela ici. Passons donc à...

Règle n°2

Pensez à appeler GC.Collect() si un événement non récurrent vient de se produire et qu'il est très probable que cet événement d'avoir causé la mort d'un grand nombre d'anciens objets mourir.

Un exemple classique de ceci est si vous êtes une application client et que vous affichez un formulaire très grand et compliqué compliqué qui a beaucoup de données associées données. Votre utilisateur vient de d'interagir avec ce formulaire en créant potentiellement créer de gros objets... des choses comme des documents XML, ou un grand DataSet ou deux. Lorsque le formulaire se ferme, ces objets sont morts et donc GC.Collect() va récupérer la mémoire associée avec eux...

Il semble donc que cette situation puisse relever de la règle n°2, vous savez qu'il y a un moment dans le temps où beaucoup de vieux objets sont morts, et c'est non récurrent. Cependant, n'oubliez pas les mots d'adieu de Rico.

La règle n°1 devrait l'emporter sur la règle n°2 sans des preuves solides.

Mesure, mesure, mesure.

10 votes

Je dirais que c'est juste le vieux truc. Rien n'est vraiment mauvais ou dangereux si vous savez ce que vous faites et donc si vous savez quand et comment le faire, ainsi que ses effets secondaires. Les choses comme ne jamais, jamais utiliser xxxx sont là pour protéger le monde des mauvais programmeurs :D

0 votes

0 votes

Je ne dis pas qu'utiliser GC.Collect est une bonne pratique. Mais c'est parfois un moyen rapide de résoudre des problèmes sans en connaître la cause réelle. C'est laid, je sais, mais ça marche, et il me semble que ce n'est pas une mauvaise approche, surtout quand on n'a pas beaucoup de temps pour trouver la cause profonde du problème et que votre patron se tient derrière vous... vous savez.

59voto

Aaron Fischer Points 8919

Si vous appelez GC.Collect() dans un code de production, vous déclarez essentiellement que vous en savez plus que les auteurs du GC. Cela peut être le cas. Mais ce n'est généralement pas le cas, et c'est donc fortement déconseillé.

4 votes

C'est tout à fait vrai, mais je ne sais pas s'ils peuvent faire des hypothèses qui s'appliquent à tous les développements.

2 votes

@Ken Non, ils ne peuvent pas. Mais êtes-vous en meilleure position pour le faire ? Ou allez-vous écrire du code en supposant un matériel spécifique, une version de système d'exploitation spécifique, etc. Le rapport douleur/gain est trop élevé sur ce point.

2 votes

@TheDag IMO bien sûr que je le suis. Lorsque je libère de la mémoire et d'autres éléments, je ne me soucie pas vraiment du matériel, car c'est au système d'exploitation de s'en occuper. Je ne me soucie pas non plus du système d'exploitation parce que j'ai une interface commune à tous ceux pour lesquels je programme. (Par exemple, je ne me soucie pas de savoir si c'est Windows, Mac ou Linux : quand j'alloue/libère de la mémoire en C/C++, c'est new/delete malloc/dealloc). Je peux toujours me tromper, alors n'hésitez pas à me corriger.

26voto

Dib Points 158

Qu'en est-il lorsque vous utilisez des objets COM comme MS Word ou MS Excel à partir de .NET ? Sans appeler GC.Collect après avoir libéré les objets COM, nous avons constaté que les instances des applications Word ou Excel existent toujours.

En fait, le code que nous utilisons est le suivant :

Utils.ReleaseCOMObject(objExcel)

' Call the Garbage Collector twice. The GC needs to be called twice in order to get the
' Finalizers called - the first time in, it simply makes a list of what is to be finalized,
' the second time in, it actually does the finalizing. Only then will the object do its 
' automatic ReleaseComObject. Note: Calling the GC is a time-consuming process, 
' but one that may be necessary when automating Excel because it is the only way to 
' release all the Excel COM objects referenced indirectly.
' Ref: http://www.informit.com/articles/article.aspx?p=1346865&seqNum=5
' Ref: http://support.microsoft.com/default.aspx?scid=KB;EN-US;q317109
GC.Collect()
GC.WaitForPendingFinalizers()
GC.Collect()
GC.WaitForPendingFinalizers()

Ce serait donc une utilisation incorrecte du ramasseur de déchets ? Si oui, comment faire pour que les objets Interop meurent ? De plus, si ce n'est pas censé être utilisé de cette façon, pourquoi l'élément GC 's Collect méthode même Public ?

4 votes

Cela ferait une excellente nouvelle question pour StackOverflow, à savoir : Comment éradiquer les instances COM sans appeler le GC. En particulier en ce qui concerne les références circulaires non gérées. C'est l'un des défis qui m'a fait hésiter à mettre à niveau mon module complémentaire Outlook VB6 vers C# (nous avons beaucoup travaillé pour développer des modèles de codage et des cas de test du côté VB qui garantissaient que les références COM étaient détruites de manière déterministe lorsqu'elles n'étaient plus nécessaires).

2 votes

Si cela s'applique aux objets COM en général, il s'agit peut-être d'un scénario valable. Mais d'emblée, je dirais que le problème vient probablement du fait que vous utilisez comme serveur COM une application client conçue pour un bureau interactif. Extrait de la base de connaissances MSDN : "Microsoft ne recommande pas actuellement, et ne prend pas en charge, l'automatisation des applications Microsoft Office à partir d'une application ou d'un composant client non interactif et non surveillé (y compris ASP, ASP.NET, DCOM et NT Services), car Office peut présenter un comportement instable et/ou un blocage lorsque Office est exécuté dans cet environnement."

2 votes

@TheDag - Microsoft ne le recommande peut-être pas, mais beaucoup d'entre nous ont dû porter du vieux code VB6 avec interopérabilité avec Office vers des applications Windows .Net. J'ai passé des mois à travailler jusqu'à ce que je me débarrasse enfin de toutes les références suspendues invisibles pour un grand projet de conversion de VB6 en .Net. Apprendre à libérer dans l'ordre d'affectation inverse et à conserver les références locales de CHAQUE objet com, y compris les collections, m'a aidé.

16voto

Jason Short Points 4034

Eh bien, le GC est une de ces choses avec lesquelles j'ai une relation d'amour et de haine. Nous avons brisé dans le passé par VistaDB et en a parlé sur son blog. Ils l'ont corrigé, mais cela prend BEAUCOUP de temps pour obtenir des corrections sur ce genre de choses.

Le CG est complexe, et une approche unique est très, très difficile à mettre en œuvre dans un domaine aussi vaste. MS a fait un assez bon travail, mais il est parfois possible de tromper la GC.

En général, vous ne devriez pas ajouter un Collect sauf si tu es sûr que tu viens de vider une tonne de mémoire et que ça va aller dans un crise de la quarantaine si le CG ne le nettoie pas maintenant.

Vous pouvez foutre en l'air toute la machine avec une série de mauvaises GC.Collect déclarations. La nécessité d'une instruction collect indique presque toujours une erreur sous-jacente plus importante. La fuite de mémoire est généralement liée aux références et à un manque de compréhension de leur fonctionnement. Ou l'utilisation de l'instruction IDisposable sur des objets qui n'en ont pas besoin et de mettre une charge beaucoup plus élevée sur le GC.

Surveillez de près le pourcentage de temps passé en GC grâce aux compteurs de performance du système. Si vous voyez que votre application utilise 20% ou plus de son temps dans la GC, vous avez de sérieux problèmes de gestion d'objets (ou un modèle d'utilisation anormal). Vous voulez toujours minimiser le temps passé par le GC car cela accélérera toute votre application.

Il est également important de noter que la GC est différente sur les serveurs et les stations de travail. J'ai vu un certain nombre de petits problèmes difficiles à localiser dus à des personnes qui ne testaient pas les deux GC (ou qui ne savaient même pas qu'il y en avait deux).

Et pour être aussi complet que possible dans ma réponse, vous devriez également tester sous Mono si vous ciblez également cette plateforme. Comme il s'agit d'une implémentation totalement différente, elle peut rencontrer des problèmes totalement différents de ceux de l'implémentation MS.

0 votes

Le coupable est souvent l'événement. Lorsqu'une méthode d'instance est utilisée comme gestionnaire d'événement, l'éditeur de l'événement a une référence à l'abonné via le délégué d'événement. La seule façon "facile" d'éviter ce problème est de n'utiliser que des éditeurs dont la durée de vie est au plus égale à celle des abonnés (par exemple, une TextBox publiant un événement géré par le formulaire contenant n'est pas un problème, car la textbox n'est pas censée vivre en dehors du formulaire). Exemple de scénario problématique : Modèle singleton, vues temporaires gérant les événements du modèle.

7 votes

Comment peut-on foutre en l'air toute la machine ?

12voto

TheZenker Points 1053

D'après mon expérience, il n'a jamais été conseillé de faire un appel à GC.Collect() dans un code de production. En débogage, oui, cela a ses avantages pour aider à clarifier les fuites de mémoire potentielles. Je pense que ma raison fondamentale est que le GC a été écrit et optimisé par des programmeurs beaucoup plus intelligents que moi, et si j'arrive à un point où je sens que j'ai besoin d'appeler GC.Collect(), c'est un indice que j'ai fait fausse route quelque part. Dans votre situation, il ne semble pas que vous ayez réellement des problèmes de mémoire, mais simplement que vous vous inquiétez de l'instabilité que la collecte apportera à votre processus. Étant donné qu'elle ne nettoie pas les objets encore utilisés et qu'elle s'adapte très rapidement aux demandes croissantes et décroissantes, je pense que vous n'aurez pas à vous en inquiéter.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X