Question

J'écris une application financière C # qui reçoit des messages du réseau, les traduit en différents objets en fonction du type de message et leur applique enfin la logique métier de l'application.

Le fait est qu'après l'application de la logique métier, je suis certain que je n'aurai plus jamais besoin de cette instance. Plutôt que d’attendre que le ramasse-miettes les libère, j’aimerais explicitement "supprimer". eux.

Existe-t-il un meilleur moyen de le faire en C #? Devrais-je utiliser un pool d'objets pour réutiliser le même ensemble d'instances ou existe-t-il une meilleure stratégie?

L’objectif étant d’empêcher la récupération de place d’utiliser un processeur au cours d’un processus critique.

Était-ce utile?

La solution

Ne les supprimez pas tout de suite. Appeler le ramasse-miettes pour chaque objet est une mauvaise idée. Normalement, vous vraiment ne voulez pas du tout gâcher le ramasse-miettes, et même les processus urgents ne sont que des conditions de concurrence imminentes s’ils se produisent si ils sont aussi sensibles.

Mais si vous savez que votre application aura des périodes de charge occupées ou de charge légère, vous pouvez essayer un GC.Collect () plus général lorsque vous atteignez une période de lumière afin d'encourager le nettoyage avant la prochaine période de charge.

Autres conseils

Regardez ici: http://msdn.microsoft.com/en-us /library/bb384202.aspx

Vous pouvez dire au ramasse-miettes que vous faites quelque chose de critique pour le moment et qu'il essayera d'être gentil avec vous.

Vous frappez vous-même - utilisez un pool d’objets et réutilisez-les. La sémantique des appels à ces objets devrait être cachée derrière une façade d'usine. Vous aurez besoin de développer la piscine d'une manière prédéfinie. Peut-être doubler la taille à chaque fois qu'il atteindra la limite - un algorithme de niveau élevé ou un pourcentage fixe. Je vous conseillerais fortement de ne pas appeler GC.Collect ().

Lorsque la charge de votre pool est suffisamment faible, vous pouvez le réduire et un déclencheur de récupération de la mémoire peut se déclencher. Laissez le CLR s'en préoccuper.

Essayer de deviner le récupérateur de déchets est généralement une très mauvaise idée. Sous Windows, le ramasse-miettes est un espace générationnel et peut compter sur pour être assez efficace. Il y a quelques exceptions notées à cette règle générale - la plus courante étant la survenue d'un événement ponctuel dont vous savez qu'il aura provoqué la mort de nombreux objets anciens - une fois que les objets sont promus en Gen2 (le plus long vécu) ils ont tendance à traîner.

Dans le cas que vous mentionnez, vous donnez l'impression que vous générez un certain nombre d'objets éphémères - ceux-ci créeront des collections Gen0. Celles-ci se produisent quand même assez souvent et sont les plus efficaces. Si vous préférez, vous pouvez les éviter en disposant d'un pool d'objets réutilisable, mais il est préférable de déterminer avec certitude si GC est un problème de performances avant de prendre une telle mesure: le profileur CLR en est l'outil.

Il convient de noter que le garbage collector est différent sur différents frameworks .NET. Sur le framework compact (qui fonctionne sur la Xbox 360 et sur les plates-formes mobiles), il s’agit d’un GC non générationnel. faites attention aux déchets générés par votre programme.

Forcer un GC.Collect () est généralement une mauvaise idée, laissez le GC faire ce qu’il fait le mieux. Il semble que la meilleure solution serait d’utiliser un pool d’objets que vous pourrez agrandir si nécessaire - j’ai utilisé ce modèle avec succès.

De cette façon, vous évitez non seulement la récupération de place, mais également le coût d'allocation habituel.

Enfin, êtes-vous sûr que le CPG vous pose un problème? Vous devriez probablement mesurer et prouver cela avant de mettre en œuvre des solutions qui vous feront gagner du temps - vous faites peut-être un travail inutile!

"L'objectif est d'éviter que le garbage collection utilise un processeur lors d'un processus critique"

Q: Si vous entendez par temps critique, vous voulez dire que vous écoutez du matériel ésotérique et que vous ne pouvez pas vous permettre de rater l'interruption?

A: Si tel est le cas, C # n'est pas le langage à utiliser, vous souhaitez Assembler, C ou C ++ pour cela.

Q: Si vous entendez par Critique critique, vous voulez dire tant qu'il y a beaucoup de messages dans le tuyau et que vous ne voulez pas laisser le récupérateur de place ralentir?

R: Si vous vous inquiétez donc inutilement. La durée de vie de vos objets est très courte. Cela signifie que le ramasse-miettes les recyclera de manière très efficace, sans retard apparent dans les performances.

Cependant, le seul moyen de savoir avec certitude est de le tester, de le configurer pour qu'il traite de manière continue le flux de messages de test. si vous pouvez le repérer, je serai encore plus surpris si cela compte vraiment).

Comprenez bien le comportement du récupérateur de déchets et découvrez-le. Vous comprendrez pourquoi ce que vous pensez ici n'est pas recommandé. sauf si vous aimez vraiment le CLR pour passer du temps à réorganiser des objets en mémoire beaucoup .

Quelle est l'intensité de l'application? J'ai écrit une application qui capture 3 cartes son (Managed DirectX, 44,1 KHz, Stéréo, 16 bits), en blocs de 8 Ko, et envoie 2 des 3 flux à un autre ordinateur via TCP / IP. L’interface utilisateur affiche un indicateur de niveau audio et un titre / artiste avec défilement (lisse) pour chacun des 3 canaux. Cela fonctionne sur les PC avec XP, 1,8 GHz, 512 Mo, etc. L'application utilise environ 5% du CPU.

Je me suis abstenu d'appeler manuellement les méthodes du GC. Mais j'ai dû régler certaines choses inutiles. J'ai utilisé le profileur Ant de RedGate pour affiner les portions inutiles. Un outil génial!

Je souhaitais utiliser un pool de tableaux d'octets pré-alloués, mais l'assemblage DX géré alloue les tampons d'octets en interne, puis les renvoie à l'application. Il s'est avéré que je n'avais pas à le faire.

Si le temps presse, utilisez une plate-forme déterministe telle que C / C ++. Même l'appel de GC.Collect () générera des cycles de processeur.

Votre question commence par la suggestion que vous souhaitez économiser de la mémoire tout en vous débarrassant des objets. C'est une optimisation critique pour l'espace. Vous devez décider de ce que vous voulez vraiment, car le GC optimise mieux cette situation qu'un humain.

À en juger par le son, il semble que vous parlez de finalisation déterministe (destructeurs en C ++), qui n’existe pas en C #. La chose la plus proche que vous trouverez en C # est le motif Jetable. En gros, vous implémentez l'interface IDisposable .

Le modèle de base est le suivant:

public class MyClass: IDisposable
{
    private bool _disposed;

    public void Dispose()
    {
        Dispose( true );
        GC.SuppressFinalize( this );
    }

    protected virtual void Dispose( bool disposing )
    {
        if( _disposed )    
            return;

        if( disposing )
        {
            // Dispose managed resources here
        }

        _disposed = true;
    }
}

Vous pouvez avoir un nombre limité d'instances de chaque type dans un pool et réutiliser les instances déjà effectuées avec des instances. La taille du pool dépend de la quantité de messages que vous allez traiter.

Au lieu de créer une nouvelle instance d'un objet à chaque fois que vous recevez un message, pourquoi ne réutilisez-vous pas les objets déjà utilisés? De cette façon, vous ne vous battrez pas contre le ramasse-miettes et la mémoire de votre tas ne sera pas fragmentée. **

Pour chaque type de message, vous pouvez créer un pool pour contenir les instances non utilisées. Chaque fois que vous recevez un message réseau, vous examinez le type de message, extrayez une instance en attente du pool approprié et appliquez votre logique métier. Ensuite, vous remettez cette instance de l'objet de message dans son pool.

Vous souhaiterez probablement "charger paresseux". votre pool avec des instances afin que votre code évolue facilement. Par conséquent, votre classe de pool devra détecter le moment où une instance nulle a été extraite et la remplir avant de la distribuer. Ensuite, lorsque le code d'appel le remet dans le pool, il s'agit d'une instance réelle.

** "Le regroupement d'objets est un modèle à utiliser qui permet aux objets d'être réutilisés plutôt que d'être alloués et désalloués, ce qui permet d'éviter la fragmentation de tas ainsi que les compactions coûteuses de GC."

http://geekswithblogs.net/robp/archive/2008/08/07/speedy-c-part-2-optimizing-memory-allocations---pool-and.aspx

En théorie, le CPG ne devrait pas fonctionner si votre processeur est soumis à une charge importante ou si cela n’est pas vraiment nécessaire. Mais si vous devez le faire, vous voudrez peut-être simplement garder tous vos objets en mémoire, peut-être une instance de singleton, et ne jamais les nettoyer à moins que vous ne soyez prêts. C’est probablement le seul moyen de garantir le fonctionnement du GC.

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