2009-02-11 5 views
2
namespace Test 
{ 
    class Test 
    { 
     delegate void HandleMessage(string message); 

     public void handleMessage(string message){} 

     static void Main(string[] args) 
     { 
      HandleMessage listener1 = new Test().handleMessage; 
      WeakReference w1 = new WeakReference(listener1); 

      HandleMessage listener2 = (message) => { }; 
      WeakReference w2 = new WeakReference(listener2); 

      Console.WriteLine("w1.Target:\t[" + w1.Target + "]"); 
      Console.WriteLine("w2.Target:\t[" + w2.Target + "]"); 

      listener1 = null; 
      listener2 = null; 
      GC.Collect(); 
      Console.WriteLine("after GC"); 

      Console.WriteLine("w1.Target:\t[" + w1.Target + "]"); 
      Console.WriteLine("w2.Target:\t[" + w2.Target + "]"); 

      Console.ReadLine(); 
     } 
    } 
} 

Pourquoi w2.Target n'est pas nul après GC?Lambda Expression car la cible de référence faible ne peut pas être GC?

 
    w1.Target:  [Test.Test+HandleMessage] 
    w2.Target:  [Test.Test+HandleMessage] 
    after GC 
    w1.Target:  [] 
    w2.Target:  [Test.Test+HandleMessage] 

EDIT

Merci pour toutes les réponses, Brian Rasmussen et Jon Skeet vos réponses sont correctes. Maintenant, je comprends parfaitement ce qui se passe, alors j'ai écrit un autre exemple pour rendre tout plus clair.

L'exemple suivant montre que:

Si # test create() ne fait pas référence à des propriétés d'instance ou des méthodes, puis "HandleMessage private static CS < $> 9__CachedAnonymousMethodDelegate1" sera créé par le compilateur, comme ce que Jon Skeet a dit - Cela le rend plus efficace lorsque vous utilisez la même expression lambda plusieurs fois.

Si le test # create() fait référence à des propriétés ou des méthodes d'instance, comme dans l'exemple ci-dessous appelant this.ToString(); alors le compilateur ne peut pas créer une méthode statique pour remplacer la logique de la méthode intstance, donc après GC, l'instance HandleMessage peut être collectée.

namespace Test 
{ 
    class Test 
    { 
     public delegate void HandleMessage(string message); 

     public void handleMessage(string message) 
     { 
     } 

     public HandleMessage create() 
     { 
      return (message) => { 
       //this.ToString(); 
      }; 
     }  

     static void Main(string[] args) 
     { 
      HandleMessage listener1 = new Test().handleMessage; 
      WeakReference w1 = new WeakReference(listener1); 

      HandleMessage listener2 = new Test().create();//(message) => { }; 
      WeakReference w2 = new WeakReference(listener2); 

      Console.WriteLine("w1.Target:\t[" + w1.Target + "]"); 
      Console.WriteLine("w2.Target:\t[" + w2.Target + "]"); 

      listener1 = null; 
      listener2 = null; 
      GC.Collect(); 
      Console.WriteLine("after GC"); 

      Console.WriteLine("w1.Target:\t[" + w1.Target + "]"); 
      Console.WriteLine("w2.Target:\t[" + w2.Target + "]"); 

      Console.ReadLine(); 
     } 
    } 
} 

Répondre

0

Le modèle commun pour la force Collect-mémoire est:

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

De plus, GC est libre de ne pas recueillir des choses :)

+0

Il n'y a pas de finaliseur, donc le code de l'OP suffira dans ce cas. –

2

L'expression lambda est mise en mémoire cache dans un champ statique la classe - quand je l'ai compilé, il était en CS$<>9__CachedAnonymousMethodDelegate1. Cela le rend plus efficace lorsque vous utilisez la même expression lambda plusieurs fois, mais cela signifie qu'il ne sera pas récupéré de la saleté. Regardez l'IL généré pour voir ce que je veux dire.

Si l'expression lambda capture des variables, je ne crois pas qu'elles seront mises en cache (parce qu'elles ne le peuvent pas!). Donc, si vous changez votre code à utiliser:

string x = "hello"; 
HandleMessage listener2 = message => Console.WriteLine(x); 

vous verrez w2.Target deviendra nulle après la collecte des ordures.

+0

Pour une raison que j'ignore, je n'ai pas reçu de «réponse de chargement» pendant la saisie. Bizarre depuis que tu as posté ta réponse 13 minutes avant moi. –

6

Cela n'a rien à voir avec le lambda. Le même comportement peut être observé pour les délégués anonymes. Donc, si vous changez le code en

HandleMessage listener2 = delegate(string message) => { }; 

vous obtenez le même résultat.

Dans le premier cas, vous disposez d'une méthode d'instance sur une instance de Test. Puisque vous n'avez aucune autre référence à cette instance lorsque listener1 est annulée, elle peut être collectée. Dans le second cas, la méthode anonyme doit être placée sur un certain type (car les méthodes ne peuvent pas exister seules). Dans ce cas, le compilateur place la méthode anonyme en tant que méthode statique sur votre classe Test. De plus, la référence est stockée dans un membre statique sur le type Test. Ainsi, Type a également une référence statique à la méthode, c'est pourquoi il survit à une collection.

Jetez un coup d'œil à l'IL pour voir comment les choses sont câblées.

Questions connexes