Y at-il une pratique courante comment faire libérer de la mémoire pour Garbage Collector plus facile dans .NET?

StackOverflow https://stackoverflow.com/questions/2714811

Question

J'ai pensé s'il y a un moyen comment accélérer libérer de la mémoire dans .NET. Je crée un jeu dans .NET (seul code managé) où aucun graphique important est nécessaire mais je voudrais écrire correctement afin de ne pas perdre la performance pour rien.

Par exemple est-il utile à la valeur null assign à des objets qui ne sont pas plus besoin? Je vois cela dans quelques échantillons sur Internet.

Était-ce utile?

La solution

  

est-il utile à la valeur null assign à des objets qui ne sont pas plus nécessaires?

En général, non. La plupart des échantillons que vous verrez en ligne qui font cela sont des gens qui sont venus de .Net VB6, où ce fut une meilleure pratique commune. Dans .Net, il est moins utile. Si vous avez une méthode très longue course, il pourrait laisser le garbage collector trouver l'objet un peu plus tôt - mais si votre méthode est que de temps vous avez d'autres problèmes.

Au lieu de cela, en .Net, vous devez construire des méthodes à court et à définir vos variables le plus tard possible dans les plus petits blocs de portée possibles. Utilisez la durée de vie « naturelle » de la variable telle que déterminée par la portée, mais gardez cette courte vie naturelle.

Vous devez pas accomplirez votre propre collection d'ordures, comme le suggère au moins une autre réponse ici. Cela peut effectivement faire des choses plus lent . .Net utilise un garbage collector générationnelle. En forçant une collection d'ordures, vous peut recueillir l'objet cible (vous aussi peut-être pas - il n'y a aucune garantie avec la collecte des ordures). Mais vous aurez probablement aussi la force d'un tas d'autres objets que vous ne pouvez pas Collect encore à stocker dans une génération d'ordre supérieur, ce qui les rend plus difficiles à recueillir à l'avenir. Il suffit donc de ne pas le faire.

Autres conseils

Vous ne devriez pas trop vous inquiéter et optimisez pas prématurément. Gestion de la mémoire dans .NET est automatique et très efficace. Si vous commencez à « optimiser » dont vous avez besoin de savoir exactement ce que vous faites ou vous pourriez ralentir.

Alors terminer votre premier jeu, puis si elle est trop lent utiliser un profileur pour trouver le problème. Très probablement, il ne sera pas un problème de mémoire.

La plupart des objets graphiques liés dans .net (image et ses descendants, graphiques, stylo, brosse, etc.) mettre en œuvre IDisposable. Si vous allez utiliser un objet pour une opération spécifique, pas encore, utilisez le schéma suivant:

using(var g = Graphics.FromBitmap(bmp))
{
    //Do some stuff with the graphics object
}

Faire comme cela garantira des ressources non gérées vont se libérer quand ils tombent hors de portée.

Je trouve l'allocation des objets dans un .net des choses qui affecte les performances les plus. Pour contourner cela, j'utilise l'un modèle d'usine où je recycle des objets que je l'ai utilisé. Est simple implémentation générique:

internal class ListFactory<T> 
    where T: IRecyclable, new()
{
    private List<T> _internalList;
    private int _pointer;

    public ListFactory()
    {
        _internalList = new List<T>();
        _pointer = 0;
    }

    public void Clear()
    {
            _pointer = 0;
    }

    public int Count
    {
        get
        {
            return _pointer;
        }
    }

    public List<T> InnerList
    {
        get
        {
            return _internalList;
        }
    }

    //Either return T form the list or add a new one
    //Clear T when it is being reused
    public T Create()
    {
        T t;

        //If the pointer is less than the object count then return a recycled object
        //Else return a new object 
        if (_pointer < _internalList.Count )
        {
            t = _internalList[_pointer];
            t.Recycle();
        }
        else
        {
            t = new T();
            _internalList.Add(t);
        }
        _pointer ++;
        return t;
    }
}

Pour mon algorithme de routage ligne, je dois garder constamment de nombreuses valeurs comme RouteNode qui implémente l'interface suivante:

public interface IRecyclable
{
    void Recycle();
}

Ces obtenir constamment créé et détruit. Pour recycler ces objets, créer une nouvelle usine:

nodeFactory = new ListFactory<RouteNode>();

Lorsque vous avez besoin d'un objet, appelez la méthode create:

RouteNode start = nodeFactory.Create();
RouteNode goal = nodeFactory.Create();

Lorsque vous avez terminé avec les objets de la liste, décochez la liste. Le pointeur est remis à zéro au début de la liste, mais les objets eux-mêmes ne sont pas détruits. En fait, la méthode de recyclage est appelée uniquement lorsque l'objet est récupéré à nouveau. (Vous voudrez peut-être faire plus tôt si vous avez d'autres références d'objets, voir les commentaires ci-dessous)

Ceci est une mise en œuvre assez naïve, mais son un endroit pour commencer.

Il peut être utile dans certains cas, à des objets définis à null qui ne sont plus nécessaires. Souvent, il est inutile ou contre-productif, mais pas toujours. Quatre principales situations où il peut être utile:

  • Les objets contenant des champs déclarés comme « WithEvents » dans vb.net devrait mettre en œuvre IDisposable et devraient Nulled ces champs dans la méthode Dispose. Je ne sais pas pourquoi vb.net ne comprend pas une façon agréable de automatiquement Nulling champs WithEvents, mais comme il ne leur faut déminer manuellement.
  • Si une variable locale contient une référence à un objet qui en fait ne sera jamais utilisé à nouveau, mais il peut être un certain temps avant que le code atteint un point où le compilateur connaît la variable ne sera pas se habituer, la compensation la variable peut permettre à la référence à libéré plus tôt.
  • Si à un moment donné un tableau qui est grand ou a été autour depuis un certain temps est connu pour être inutile, et si les références à des objets récemment attribués sont stockés en elle, la compensation de ces références avant de détruire tous les survivants les références au tableau peuvent permettre aux objets nouvellement créés pour être libérés beaucoup plus tôt qu'ils ne le seraient autrement.
  • Si un objet a une routine Finaliser et est dans la file d'attente de finalisation, tous les objets référencés directement ou indirectement par celle-ci seront exemptés de la collecte des ordures. Par conséquent, les objets finalisables ne doivent pas faire référence à tout ce qui ne sera pas nécessaire pour la finalisation.

        
  • Pour ce faire (et je l'ai dû faire avec de fréquentes> allocations de 50 Mo), appel:

            myObj = null;
            GC.Collect();
            GC.WaitForPendingFinalizers();
    

    J'ai remarqué que l'empreinte mémoire de l'application sera alors grandement diminuer. En théorie, vous ne devriez pas avoir à le faire. Cependant, dans la pratique, avec 32 fenêtres de bit OS vous pourriez obtenir 2 blocs contigus de> 300MB libre à tout moment, et ayant l'espace pris par beaucoup de petites allocations ou une série de gros peut signifier que d'autres allocations grandes se ne pas inutilement. Le garbage collector fonctionne en arrière-plan quand il le peut, mais si vous devez absolument faire de grandes allocations en ce moment, cet ensemble de lignes aide à rendre cela possible pour moi.

    EDIT:. D'après ce que je mets dans les commentaires, les downvoters

    Si vous lisez l'ensemble post sur la collecte des ordures par Rico Mariani , vous remarquerez que les grandes, peu fréquentes, les allocations de mémoire non prévisibles tombent dans le scénario n ° 2. Pour Whit:

      

    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 probablement   ont causé beaucoup de vieux objets   dé.

         

    Un exemple classique est si vous êtes   écrire une application client et vous   afficher une très grande et complexe   forme qui a beaucoup de données associées   avec ça. Votre nom d'utilisateur a juste   interagi avec cette forme potentiellement   créer des objets de grande taille ... les choses   comme des documents XML, ou un grand DataSet   ou deux. Lorsque le formulaire se ferme ces   les objets sont morts et donc GC.Collect ()   va récupérer la mémoire associée   avec eux.

         

    Maintenant, pourquoi devrais-je suggérer cela comme   temps possible d'appeler le collecteur?   Je veux dire, mon conseil habituel va quelque chose   comme « le collecteur est auto-réglage si   ne plaisante pas avec elle. » Pourquoi le changement   d'attitude que vous pourriez demander?

         

    Eh bien est une situation ici où la   Le collecteur de tendancy [sic] pour essayer de prédire   l'avenir basé sur le passé est probable   à l'échec.

    Si vous faites de grandes allocations dans votre jeu, vous devez être prudent sur la façon dont la mémoire est gérée. Les travaux de garbage collector sur la prévision en fonction des événements passés, et les grands blocs de mémoire sur une machine 32 bits peut être dévastateur pour les allocations futures si pas correctement géré. Si vous ne l'avez pas fait, ne présumez pas automatiquement que je me trompe; si vous l'avez fait, je serais heureux d'expliquer comment le faire correctement (comment la mémoire de defrag pour vous assurer que je peux toujours l'allocation 50-100mb de la mémoire à un moment donné).

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