Question

Bien que je comprenne les sérieuses implications de l'utilisation de cette fonction (ou du moins c'est ce que je pense), je ne vois pas pourquoi c'est devenu l'une de ces choses que les programmeurs respectables n'utiliseraient jamais, même ceux qui ne le font pas. t même savoir à quoi il sert.

Supposons que je développe une application dans laquelle l'utilisation de la mémoire varie énormément en fonction de ce que l'utilisateur fait. Le cycle de vie d'une application peut être divisé en deux étapes principales: l'édition et le traitement en temps réel. Pendant la phase d'édition, supposons que des milliards voire des milliards d'objets soient créés; certaines sont petites, d'autres non, certaines peuvent avoir des finaliseurs et d'autres non, et supposent que leur durée de vie varie de quelques millisecondes à de longues heures. Ensuite, l'utilisateur décide de passer à l'étape en temps réel. À ce stade, supposons que la performance joue un rôle fondamental et que la moindre modification dans le déroulement du programme puisse avoir des conséquences catastrophiques. La création d’objets est ensuite réduite au minimum en utilisant des pools d’objets, etc., mais le GC intervient de manière inattendue et rejette tout, et quelqu'un meurt.

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

Après tout, ces deux étapes ne se chevauchent jamais et toutes les optimisations et statistiques que le GC aurait pu collecter seraient peu utiles ici ...

Remarque: comme certains d'entre vous l'ont souligné, .NET n'est peut-être pas la meilleure plate-forme pour une application de ce type, mais cela dépasse le cadre de cette question. Le but est de préciser si un appel de GC.Collect () peut améliorer le comportement / les performances globaux d'une application. Nous sommes tous d’accord sur le fait 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.

Merci.

Était-ce utile?

La solution

Sur le blog de Rico ...

  

Règle n ° 1

     

Ne pas.

     

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

     

Règle n ° 2

     

Pensez à appeler GC.Collect () si certains   événement non récurrent vient de se produire   et cet événement est très susceptible de   ont causé beaucoup d'objets anciens à   mourir.

     

Un exemple classique de ceci est si vous êtes   écrire une application client et vous   afficher un très grand et compliqué   forme qui a beaucoup de données associées   avec ça. Votre utilisateur vient de   en interaction avec cette forme potentiellement   créer de gros objets ... des choses   comme des documents XML ou un grand ensemble de données   ou deux. Quand le formulaire ferme ces   les objets sont morts et donc GC.Collect ()   va récupérer la mémoire associée   avec eux ...

Il semble donc que cette situation relève de la règle n ° 2, vous savez qu'il y a un moment où de nombreux objets anciens sont morts, et c'est non récurrent. Cependant, n'oubliez pas les derniers mots de Rico.

  

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

Mesurer, mesurer, mesurer.

Autres conseils

Si vous appelez GC.Collect () dans le code de production, vous déclarez essentiellement que vous en savez plus que les auteurs du catalogue global. C'est peut-être le cas. Cependant, ce n’est généralement pas le cas et donc fortement découragé.

Et si vous utilisiez des objets COM tels que 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 d'application Word ou Excel existaient 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()

Alors, s'agirait-il d'une utilisation incorrecte du ramasse-miettes? Si oui, comment pouvons-nous faire mourir les objets Interop? De plus, si ce n’est pas censé être utilisé de la sorte, pourquoi la méthode Collect de GC est-elle même Public ?

Eh bien, le GC est l’une de ces choses avec lesquelles j’ai une relation amour / haine. Nous avons l'a cassé dans le passé via VistaDB et a blogué à ce sujet. Ils l'ont corrigé, mais cela prend beaucoup de temps pour obtenir des corrections sur ce genre de choses.

Le GC est complexe et une approche universelle est extrêmement difficile à obtenir pour un projet de cette envergure. MS a fait un assez bon travail, mais il est parfois possible de tromper le GC.

En général, vous ne devriez pas ajouter de Collect à moins que vous sachiez que vous venez de vider une tonne de mémoire et que cela passera à un crise du milieu de la vie si le CG n'est pas nettoyé maintenant.

Vous pouvez bousiller toute la machine avec une série de mauvaises instructions GC.Collect . La nécessité d'une instruction de collecte pointe presque toujours vers une erreur sous-jacente plus importante. La fuite de mémoire est généralement liée à des références et à un manque de compréhension de leur fonctionnement. Ou en utilisant IDisposable sur des objets qui n'en ont pas besoin et en plaçant une charge beaucoup plus lourde sur le CPG.

Surveillez de près le pourcentage de temps passé en GC via les compteurs de performance du système. Si votre application utilise 20% ou plus de son temps sur le CPG, vous rencontrez de graves problèmes de gestion des objets (ou un modèle d'utilisation anormal). Vous voulez toujours minimiser le temps passé par le CPG, car cela accélérera l'application dans son intégralité.

Il est également important de noter que le CPG est différent des serveurs sur les serveurs. J'ai vu un certain nombre de petits problèmes difficiles à déceler avec des personnes ne testant pas les deux (ou ne sachant même pas que ce sont deux personnes).

Et pour que ma réponse soit aussi complète que possible, vous devriez également tester sous Mono si vous ciblez également cette plate-forme. Étant donné qu’il s’agit d’une mise en œuvre totalement différente, la mise en oeuvre de MS peut rencontrer des problèmes totalement différents.

Il y a des situations où cela est utile, mais en général, cela devrait être évité. Vous pouvez le comparer à GOTO ou à une mobylette: vous le faites quand vous en avez besoin, mais vous n'en parlez pas à vos amis.

De par mon expérience, il n’a jamais été recommandé d’appeler GC.Collect () dans le code de production. En débogage, oui, cela a ses avantages pour aider à clarifier les fuites de mémoire potentielles. Je suppose que ma raison fondamentale est que le GC a été écrit et optimisé par les programmeurs beaucoup plus intelligemment que moi, et si j’arrive à un point où j’ai le sentiment que j’ai besoin d’appeler GC.Collect (), c’est un indice que je suis parti quelque part. Dans votre cas, il ne semble pas que vous ayez des problèmes de mémoire, vous vous inquiétez de l’instabilité que la collection apportera à votre processus. Étant donné que cela ne nettoiera pas les objets encore en usage et qu'il s'adapte très rapidement aux demandes croissantes et décroissantes, je pense que vous n'aurez pas à vous en préoccuper.

L'une des principales raisons d'appeler GC.Collect (), c'est lorsque vous venez de réaliser un événement important qui crée beaucoup de déchets, tels que ce que vous décrivez. Appeler GC.Collect () peut être une bonne idée ici; sinon, le GC pourrait ne pas comprendre qu'il s'agissait d'un événement "ponctuel".

Bien sûr, vous devriez le profiler et voir par vous-même.

Eh bien, vous ne devriez évidemment pas écrire de code avec des exigences en temps réel dans des langues avec une récupération de place non-temps réel.

Dans les cas où les étapes sont bien définies, le déclenchement du ramasse-miettes ne pose aucun problème. Mais cette affaire est extrêmement rare. Le problème, c’est que de nombreux développeurs vont essayer de s’en servir pour résoudre des problèmes de style culte du cargo, et que l’ajouter sans discernement causera des problèmes de performances.

L'appel de GC.Collect () oblige le CLR à parcourir une pile pour voir si chaque objet peut vraiment être libéré en vérifiant les références. Cela affectera l'évolutivité si le nombre d'objets est élevé et si on sait également qu'il déclenche trop souvent le nettoyage de la mémoire. Faites confiance au CLR et laissez le ramasse-miettes s’exécuter lui-même le cas échéant.

Création d’images en boucle - même si vous appelez disposer, la mémoire n’est pas récupérée. Les ordures se rassemblent à chaque fois. Je suis passé de 1,7 Go de mémoire de mon application de traitement photo à 24 Mo et les performances sont excellentes.

Vous avez absolument le temps d'appeler GC.Collect.

Enfait, je ne pense pas que ce soit une très mauvaise pratique d'appeler GC.Collect.
Il peut y avoir des cas où nous en avons besoin. Par exemple, j’ai un formulaire qui exécute un fil, ce qui permet d’ouvrir différentes tables d’une base de données, d’extraire le contenu d’un champ BLOB dans un fichier temporaire, de chiffrer le fichier, puis de lire le fichier dans un flux binaryst puis de nouveau dans un BLOB. champ dans une autre table.

L'ensemble de l'opération nécessite beaucoup de mémoire et le nombre de lignes et la taille du contenu des fichiers dans les tableaux ne sont pas certains.

J'avais souvent des exceptions OutofMemory Exception et je pensais qu'il serait sage d'exécuter périodiquement GC.Collect en fonction d'une variable de compteur. J'incrémente un compteur et lorsqu'un niveau spécifié est atteint, GC est appelé pour collecter toutes les ordures éventuellement formées et pour récupérer la mémoire perdue à cause de fuites de mémoire imprévues.

Après cela, je pense que cela fonctionne bien, du moins sans exception !!!
J'appelle de la manière suivante:

var obj = /* object utilizing the memory, in my case Form itself */
GC.Collect(GC.GetGeneration(obj ,GCCollectionMode.Optimized).

Sous .net, le temps requis pour effectuer une récupération de place est beaucoup plus étroitement lié à la quantité de choses qui ne sont pas des déchets, qu'à la quantité de choses qui sont. En effet, à moins qu’un objet ne remplace Finalize (soit explicitement, soit via un destructeur C #), est la cible d’un WeakReference , reste sur le tas de gros objets ou est spécial dans certains cas. En d’autres termes, la seule chose qui identifie la mémoire dans laquelle elle se trouve en tant qu’objet est l’existence de références enracinées à celle-ci. Autrement, le fonctionnement du GC revient à prendre d’un bâtiment tout ce qui a de la valeur, à le dynamiter, à en construire un nouveau sur le site de l’ancien et à y placer tous les objets de valeur. L’effort requis pour dynamiter le bâtiment est totalement indépendant de la quantité de déchets qu’il contient.

Par conséquent, appeler GC.Collect est susceptible d’augmenter la charge de travail globale du système. Cela retardera l'apparition de la prochaine collection, mais fera probablement autant de travail immédiatement que la prochaine collection l'aurait demandé lorsqu'elle s'est produite; au moment où la prochaine collecte aurait eu lieu, le temps total consacré à la collecte aura été à peu près identique à celui où GC.Collect n'aurait pas été appelé, mais le système aura accumulé des déchets, la collection suivante sera requise plus tôt que GC.Collect n'a pas été appelé.

Les fois où je peux voir que GC.Collect est vraiment utile, c’est quand il faut mesurer l’utilisation de la mémoire de certains codes (car les chiffres d’utilisation de la mémoire ne sont vraiment significatifs qu'après une collection), ou le profil lequel de plusieurs algorithmes est préférable (appeler GC.Collect () avant d'exécuter chacun de plusieurs morceaux de code peut aider à garantir un état de base cohérent). Il y a quelques autres cas où l'on pourrait savoir ce que le GC ne sait pas, mais à moins d'écrire un programme à un seul thread, il est impossible de savoir qu'un appel GC.Collect serait utile. Les structures de données d'un fil évitent la "crise de la mi-vie" ne provoquerait pas une "crise de mi-vie" dans les données des autres threads. qui auraient autrement été évités.

Nous avons eu un problème similaire avec le ramasse-miettes qui ne récupère pas les ordures et ne libère pas de mémoire.

Dans notre programme, nous traitions des feuilles de calcul Excel de taille modeste avec OpenXML. Les feuilles de calcul contenaient entre 5 et 10 "feuilles". avec environ 1000 lignes de 14 colonnes.

Le programme dans un environnement 32 bits (x86) planterait avec un "manque de mémoire". Erreur. Nous l'avons fait fonctionner dans un environnement x64, mais nous voulions une meilleure solution.

Nous en avons trouvé un.

Voici quelques fragments de code simplifiés décrivant ce qui n'a pas fonctionné et ce qui a fonctionné lorsqu'il a appelé explicitement le récupérateur de place afin de libérer de la mémoire des objets supprimés.

L'appel du CPG depuis l'intérieur du sous-programme n'a pas fonctionné. La mémoire n'a jamais été récupérée ...

 
For Each Sheet in Spreadsheets
    ProcessSheet(FileName,sheet)
Next

Private Sub ProcessSheet(ByVal Filename as string, ByVal Sheet as string)
    ' open the spreadsheet 
    Using SLDoc as SLDocument = New SLDocument(Filename, Sheet)
        ' do some work....
        SLDoc.Save
    End Using
    GC.Collect()
    GC.WaitForPendingFinalizers()
    GC.Collect()
    GC.WaitForPendingFinalizers()
End Sub

En déplaçant l’appel du GC en dehors de la portée du sous-programme, les déchets ont été collectés et la mémoire libérée.

 
For Each Sheet in Spreadsheets
    ProcessSheet(FileName,sheet)
    GC.Collect()
    GC.WaitForPendingFinalizers()
    GC.Collect()
    GC.WaitForPendingFinalizers()
Next

Private Sub ProcessSheet(ByVal Filename as string, ByVal Sheet as string)
    ' open the spreadsheet 
    Using SLDoc as SLDocument = New SLDocument(Filename, Sheet)
        ' do some work....
        SLDoc.Save
    End Using
End Sub

J'espère que cela aidera les personnes frustrées par la récupération .NET lorsqu'il apparaît qu'il ignore les appels à GC.Collect () .

Paul Smith

Rien ne va pas avec l’appel explicite d’une collection. Certaines personnes veulent vraiment croire que s'il s'agit d'un service fourni par le fournisseur, ne le remettez pas en question. Oh, et tous ces gels aléatoires au mauvais moment de votre application interactive? La prochaine version le rendra meilleur!

Laisser un processus d’arrière-plan traiter de la manipulation de la mémoire signifie ne pas avoir à le gérer nous-mêmes, c’est vrai. Mais cela ne signifie pas logiquement qu'il est préférable pour nous de ne pas en traiter nous-mêmes en toutes circonstances. Le CPG est optimisé pour la plupart des cas. Mais cela ne signifie pas logiquement qu'il soit optimisé dans tous les cas.

Avez-vous déjà répondu à une question ouverte telle que "quel est le meilleur algorithme de tri" avec une réponse définitive? Si c'est le cas, ne touchez pas le GC. Pour ceux d'entre vous qui ont demandé les conditions ou donné des réponses de type "dans ce cas", vous pouvez en savoir plus sur le CG et sur le moment de l'activer.

Je dois dire que les applications de Google Chrome et de Firefox se sont figées, ce qui me décourage énormément. Même dans certains cas, la mémoire augmente sans problème - si seulement ils apprenaient à appeler le ramasse-miettes - ou m'a donné un bouton pour pouvoir commencer à lire le texte d'une page et ainsi éviter les blocages pendant les 20 prochaines minutes.

Je pense que vous avez raison à propos du scénario, mais je ne suis pas sûr de l'API.

Microsoft indique que, dans ce cas, vous devez ajouter une pression de mémoire. pour indiquer au GC qu'il devrait bientôt effectuer une collecte.

Quel est le problème avec ça? Le fait que vous remettiez en cause le récupérateur de mémoire et l'allocateur de mémoire, qui entre eux ont une idée beaucoup plus précise de l'utilisation réelle de la mémoire de votre application au moment de l'exécution.

Le désir d'appeler GC.Collect () tente généralement de couvrir les erreurs que vous avez commises ailleurs!

Il serait préférable que vous trouviez où vous avez oublié de disposer de choses dont vous n'aviez plus besoin.

En bout de ligne, vous pouvez profiler l’application et voir comment ces collections supplémentaires affectent les choses. Je vous conseillerais de ne pas y participer, à moins que vous ne présentiez votre profil. Le GC est conçu pour prendre soin de lui-même et, à mesure que le temps d'exécution évolue, il peut augmenter l'efficacité. Vous ne voulez pas qu'un tas de code traîne en longueur qui pourrait gâcher le travail et ne pas être en mesure de tirer parti de ces améliorations. Il existe un argument similaire pour utiliser foreach au lieu de pour, à savoir que des améliorations futures sous les couvertures peuvent être ajoutées à foreach et que votre code n'a pas à être modifié pour en tirer parti.

Le .NET Framework n’a jamais été conçu pour s’exécuter dans un environnement en temps réel. Si vous avez vraiment besoin d'un traitement en temps réel, vous utiliserez un langage incorporé non basé sur .NET ou le .NET Compact Framework s'exécutant sur un périphérique Windows CE.

Le pire, c’est que votre programme gèle un peu. Donc, si ça vous va, faites-le. Généralement, cela n’est pas nécessaire pour les applications client lourd ou les applications Web avec principalement une interaction utilisateur.

J'ai constaté que, parfois, les programmes avec des threads ou des programmes de traitement longs, obtenaient une exception OutOfMemory, même s'ils disposaient des objets correctement. Je me souviens que celui-ci était un traitement de transaction de base de données métier; l'autre était une routine d'indexation sur un thread d'arrière-plan dans une application client lourd.

Dans les deux cas, le résultat était simple: pas de GC.Collect, manque de mémoire, régulièrement; GC.Collect, une performance sans faille.

Je l'ai essayé plusieurs fois pour résoudre des problèmes de mémoire, mais en vain. Je l'ai sorti.

En bref, ne le mettez pas sauf si vous obtenez des erreurs. Si vous le mettez et que cela ne résout pas le problème de mémoire, retirez-le. N'oubliez pas de tester en mode Release et de comparer des pommes avec des pommes.

Le seul problème, c’est que vous deveniez moraliste. Ce n'est pas une question de valeurs; beaucoup de programmeurs sont morts et sont allés droit au paradis avec de nombreux GC.Collectes non nécessaires dans leur code, qui leur survit.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top