31 votes

Le garbage collector .NET effectue-t-il une analyse prédictive du code?

OK, je sais que cette question peut sembler bizarre, mais je viens de remarquer quelque chose qui vraiment me laisse perplexe... regardez ce code :

static void TestGC()
{
        object o1 = new Object();
        object o2 = new Object();
        WeakReference w1 = new WeakReference(o1);
        WeakReference w2 = new WeakReference(o2);

        GC.Collect();

        Console.WriteLine("o1 is alive: {0}", w1.IsAlive);
        Console.WriteLine("o2 is alive: {0}", w2.IsAlive);
}

Depuis o1 et o2 sont toujours dans la portée lors de la collecte des ordures se produit, je me serais attendu à la sortie suivante:

o1 est vivant: Vrai
o2 est vivant: Vrai

Mais au lieu de cela, voici ce que j'ai:

o1 est vivant: Faux
o2 est vivant: Faux

REMARQUE : ce problème se produit uniquement lorsque le code est compilé en mode Release et d'exécution à l'extérieur du débogueur

Ma conjecture est que le GC détecte qu' o1 et o2 ne sera pas utilisé à nouveau avant d'aller hors de portée, et les rassemble au début. Pour valider cette hypothèse, j'ai ajouté la ligne suivante à la fin de l' TestGC méthode :

string s = o2.ToString();

Et j'ai obtenu le résultat suivant :

o1 est vivant: Faux
o2 est vivant: Vrai

Donc, dans ce cas, o2 n'est pas collecté.

Quelqu'un pourrait jeter quelque lumière sur ce qui se passe ? Est-ce lié à optimisations JIT ? Ce qui se passe exactement ?

23voto

Lasse V. Karlsen Points 148037

Le Garbage Collector s'appuie sur les informations compilées dans votre assemblée fournie par le compilateur JIT qui lui indique quel est le code des plages d'adresses différentes variables et les "choses" sont encore en cours.

En tant que tel, dans votre code, car vous n'utilisez plus l'objet des variables GC est gratuit pour les recueillir. WeakReference n'empêchera pas cela, en fait, c'est le point de l'ensemble d'une WR, pour vous permettre de garder une référence à un objet, tout en n'empêchant pas d'être recueillies.

Le cas sur WeakReference objets est bien résumé dans la ligne la description sur le site MSDN:

Représente une référence faible, qui fait référence à un objet tout en permettant de cet objet par le garbage collection.

Le WeakReference les objets ne sont pas des ordures collectées, de sorte que vous pouvez utiliser en toute sécurité des personnes, mais les objets qu'ils désignent n'avait que le WR de référence de la gauche, et donc gratuit pour les recueillir.

Lors de l'exécution de code via le débogueur, les variables sont artificiellement étendre le champ d'application de durer jusqu'à ce que leur champ d'application se termine, généralement à la fin du bloc elles sont déclarées dans (des méthodes), de sorte que vous pouvez les contrôler à un point d'arrêt.

Il y a quelques petites choses à découvrir avec cette. Considérons le code suivant:

using System;

namespace ConsoleApplication20
{
    public class Test
    {
        public int Value;

        ~Test()
        {
            Console.Out.WriteLine("Test collected");
        }

        public void Execute()
        {
            Console.Out.WriteLine("The value of Value: " + Value);

            GC.Collect();
            GC.WaitForPendingFinalizers();
            GC.Collect();

            Console.Out.WriteLine("Leaving Test.Execute");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Test t = new Test { Value = 15 };
            t.Execute();
        }
    }
}

Dans la Version en mode, exécutée sans un débogueur, voici le résultat:

La valeur de la valeur: 15
Test recueillies
Laissant De Test.Exécuter

La raison pour cela est que même si vous êtes toujours en cours d'exécution à l'intérieur d'une méthode associée à l'objet de l'Essai, au point de demander à GC pour le faire, il n'est pas nécessaire pour toute instance de références pour Tester (pas de référence à l' this ou Value), et pas des appels à n'importe quelle instance de la méthode de gauche à effectuer, de sorte que l'objet est sûr de recueillir.

Ce qui peut avoir des effets secondaires désagréables si vous n'êtes pas au courant de ça.

Considérons la classe suivante:

public class ClassThatHoldsUnmanagedResource : IDisposable
{
    private IntPtr _HandleToSomethingUnmanaged;

    public ClassThatHoldsUnmanagedResource()
    {
        _HandleToSomethingUnmanaged = (... open file, whatever);
    }

    ~ClassThatHoldsUnmanagedResource()
    {
        Dispose(false);
    }

    public void Dispose()
    {
        Dispose(true);
    }

    protected virtual void Dispose(bool disposing)
    {
        (release unmanaged resource here);
        ... rest of dispose
    }

    public void Test()
    {
        IntPtr local = _HandleToSomethingUnmanaged;

        // DANGER!

        ... access resource through local here
    }

À ce point, que si le Test n'utilise pas n'importe quelle instance de données après avoir retenu une copie de la non géré la poignée? Que faire si GC fonctionne maintenant au point où j'ai écrit "DANGER"? Voyez-vous où cela va? Lors de la GC s'exécute, il va exécuter l'outil de finalisation, qui va tirer l'accès à la ressource non managée sous Test, qui est toujours en cours d'exécution.

Les ressources non managées, généralement accessible par un IntPtr ou similaire, est opaque pour le garbage collector, et il ne les considère pas pour juger de la vie d'un objet.

En d'autres termes, que nous de conserver une référence à la poignée dans une variable locale est dénué de sens, à la GC, c'seuls les avis qu'il n'existe pas d'instance références gauche, et considère donc que l'objet sûr de recueillir.

Ce si sûr suppose qu'il n'existe pas en dehors de la référence à l'objet qui est toujours considéré comme "vivant". Par exemple, si la classe ci-dessus a été utilisé à partir d'une méthode comme ceci:

public void DoSomething()
{
    ClassThatHoldsUnmanagedResource = new ClassThatHoldsUnmanagedResource();
    ClassThatHoldsUnmanagedResource.Test();
}

Ensuite, vous avez exactement le même problème.

(bien sûr, vous ne devriez probablement pas l'utiliser comme ça, car il met en œuvre IDisposable, vous devriez être en utilisant un using-bloc ou en appelant Dispose manuellement.)

La façon correcte d'écrire la méthode ci-dessus est de faire valoir que la GC ne recueille notre objet alors que nous en avons toujours besoin:

public void Test()
{
    IntPtr local = _HandleToSomethingUnmanaged;

    ... access resource through local here

    GC.KeepAlive(this); // won't be collected before this has executed
}

12voto

Hans Passant Points 475940

Le garbage collector obtient la durée de vie des astuces de le compilateur JIT. Il sait donc que, lors de la GC.Collect() appel il n'y a plus de références possibles pour les variables locales et que par conséquent, ils peuvent être collectées. Examen de la GC.KeepAlive()

Lorsqu'un débogueur est attaché, JIT, l'optimisation est désactivé et la durée de vie de l'indice est agrandi à la fin de la méthode. Rend le débogage beaucoup plus simple.

J'ai écrit beaucoup de réponse plus détaillée à ce sujet, vous trouverez ici.

0voto

mathk Points 2700

Je ne suis pas un C# expert, mais je dirais que c'est parce que dans la production de votre code est de les optimiser en

static void TestGC()
{
        WeakReference w1 = new WeakReference(new Object());
        WeakReference w2 = new WeakReference(new Object());

        GC.Collect();

        Console.WriteLine("o1 is alive: {0}", w1.IsAlive);
        Console.WriteLine("o2 is alive: {0}", w2.IsAlive);
}

Pas plus o1, o2 liaison rester.

EDIT: C'est une optimisation du Compilateur nommé constantes. Ce qui peut être fait avec ou sans JIT.

Si vous avez un moyen de désactiver l'équipe, mais a gardé la libération de l'optimisation, tu vas avoir le même comportement.

Les gens devraient prêter attention à la remarque:

REMARQUE : ce problème se produit uniquement lorsque le code est compilé en mode Release et exécuter en dehors du débogueur

c'est la clé.

PS: je suis aussi en supposant que votre comprendre ce WeakReference veux dire.

0voto

Jon Harrop Points 26951

Quelqu'un pourrait jeter quelque lumière sur ce qui se passe?

Votre modèle mental de la façon dont la machine virtuelle ordures collecte des ressources est trop simple. En particulier, votre hypothèse que les variables et la portée sont en quelque sorte pertinent à la collecte des déchets est faux.

Est-ce lié à optimisations JIT?

Oui.

Ce qui se passe exactement?

L'équipe n'a pas pris la peine de garder des références inutile donc de retracer le collecteur n'ai pas trouvé de références quand il donné un coup de pied de sorte qu'il a recueilli les objets.

Notez que d'autres réponses ont indiqué que le JIT transmis cette information à la GC, la vérité est en fait que l'équipe n'a pas de transmettre ces références à la GC car il n'y aurait aucun intérêt à le faire.

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