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
}