MSDN > Home page del forum > Visual C# Language > Lambda Expression cause weakreference's target cannot be GC?
Formula una domandaFormula una domanda
 

Con rispostaLambda Expression cause weakreference's target cannot be GC?

  • mercoledì 11 febbraio 2009 8.34sailing8036 Medaglie utenteMedaglie utenteMedaglie utenteMedaglie utenteMedaglie utente
     Contiene codice

    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();
            }
        }
    }

    why w2.Target is not null after GC.
    ---------------------------------------------------------
    w1.Target:      [Test.Test+HandleMessage]
    w2.Target:      [Test.Test+HandleMessage]
    after GC
    w1.Target:      []
    w2.Target:      [Test.Test+HandleMessage]

Risposte

  • venerdì 13 febbraio 2009 7.58Harry ZhuMSFT, ModeratoreMedaglie utenteMedaglie utenteMedaglie utenteMedaglie utenteMedaglie utente
     Con rispostaContiene codice
    Hi,

    The Lambda Expression is defined as a private static delegate,and then pushed onto stack . when listener2 is set to null , the Lambda is still referenced , and won't be collected.

    Please check that in the Il code using IL Disassembler.

    If we the code of Main is :
    static void Main(string[] args)  
            {  
                HandleMessage listener1 = new Test().handleMessage;  
                
     
                HandleMessage listener2 = (message) => { };  
             }   
                 

    The il code would be:
    .method private hidebysig static void  Main(string[] args) cil managed  
    {  
      .entrypoint  
      // Code size       51 (0x33)  
      .maxstack  3  
      .locals init ([0] class linq_relating_to_weak_reference.Test/HandleMessage listener1,  
               [1] class linq_relating_to_weak_reference.Test/HandleMessage listener2)  
      IL_0000:  nop  
      IL_0001:  newobj     instance void linq_relating_to_weak_reference.Test::.ctor()  
      IL_0006:  ldftn      instance void linq_relating_to_weak_reference.Test::handleMessage(string)  
      IL_000c:  newobj     instance void linq_relating_to_weak_reference.Test/HandleMessage::.ctor(object,  
                                                                                                   native int)  
      IL_0011:  stloc.0  
      IL_0012:  ldsfld     class linq_relating_to_weak_reference.Test/HandleMessage linq_relating_to_weak_reference.Test::'CS$<>9__CachedAnonymousMethodDelegate1'  
      IL_0017:  brtrue.s   IL_002c  
      IL_0019:  ldnull  
      IL_001a:  ldftn      void linq_relating_to_weak_reference.Test::'<Main>b__0'(string)  
      IL_0020:  newobj     instance void linq_relating_to_weak_reference.Test/HandleMessage::.ctor(object,  
                                                                                                   native int)  
      IL_0025:  stsfld     class linq_relating_to_weak_reference.Test/HandleMessage linq_relating_to_weak_reference.Test::'CS$<>9__CachedAnonymousMethodDelegate1'  
      IL_002a:  br.s       IL_002c  
      IL_002c:  ldsfld     class linq_relating_to_weak_reference.Test/HandleMessage linq_relating_to_weak_reference.Test::'CS$<>9__CachedAnonymousMethodDelegate1'  
      IL_0031:  stloc.1  
      IL_0032:  ret  
    } // end of method Test::Main  
     

    Code on line 12 shows:

    ldsfld pushes the value of a static field onto the evaluation stack.

    'CS$<>9__CachedAnonymousMethodDelegate1'  is the static field generated by compiler.

    then on line 25 ,

    listener2 is set to the compiler generated delegate.


    Best regards,
    Harry

  • venerdì 13 febbraio 2009 15.12sailing8036 Medaglie utenteMedaglie utenteMedaglie utenteMedaglie utenteMedaglie utente
     Con risposta

    Thank Harry for your correct reply.

    http://stackoverflow.com/questions/535972/lambda-expression-cause-weakreferences-target-cannot-be-gc
    I also got the answer from above。

    The following example show that :

    if Test#create() method don't reference to any Test instance object's properties or methods, then "private static HandleMessage CS$<>9__CachedAnonymousMethodDelegate1" will be created by compiler, like what Jon Skeet has said - That makes it more efficient when you use the same lambda expression multiple times.

    if Test#create() method reference to any Test instance object's properties or methods, like the example below calling this.toString(); then compiler can not create static method to replace the intstance's method logic, so after GC the HandleMessage instance can be collected.

    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();
       
    }
    }

    }

    Best regards,
    Eric

Tutte le risposte

  • venerdì 13 febbraio 2009 7.58Harry ZhuMSFT, ModeratoreMedaglie utenteMedaglie utenteMedaglie utenteMedaglie utenteMedaglie utente
     Con rispostaContiene codice
    Hi,

    The Lambda Expression is defined as a private static delegate,and then pushed onto stack . when listener2 is set to null , the Lambda is still referenced , and won't be collected.

    Please check that in the Il code using IL Disassembler.

    If we the code of Main is :
    static void Main(string[] args)  
            {  
                HandleMessage listener1 = new Test().handleMessage;  
                
     
                HandleMessage listener2 = (message) => { };  
             }   
                 

    The il code would be:
    .method private hidebysig static void  Main(string[] args) cil managed  
    {  
      .entrypoint  
      // Code size       51 (0x33)  
      .maxstack  3  
      .locals init ([0] class linq_relating_to_weak_reference.Test/HandleMessage listener1,  
               [1] class linq_relating_to_weak_reference.Test/HandleMessage listener2)  
      IL_0000:  nop  
      IL_0001:  newobj     instance void linq_relating_to_weak_reference.Test::.ctor()  
      IL_0006:  ldftn      instance void linq_relating_to_weak_reference.Test::handleMessage(string)  
      IL_000c:  newobj     instance void linq_relating_to_weak_reference.Test/HandleMessage::.ctor(object,  
                                                                                                   native int)  
      IL_0011:  stloc.0  
      IL_0012:  ldsfld     class linq_relating_to_weak_reference.Test/HandleMessage linq_relating_to_weak_reference.Test::'CS$<>9__CachedAnonymousMethodDelegate1'  
      IL_0017:  brtrue.s   IL_002c  
      IL_0019:  ldnull  
      IL_001a:  ldftn      void linq_relating_to_weak_reference.Test::'<Main>b__0'(string)  
      IL_0020:  newobj     instance void linq_relating_to_weak_reference.Test/HandleMessage::.ctor(object,  
                                                                                                   native int)  
      IL_0025:  stsfld     class linq_relating_to_weak_reference.Test/HandleMessage linq_relating_to_weak_reference.Test::'CS$<>9__CachedAnonymousMethodDelegate1'  
      IL_002a:  br.s       IL_002c  
      IL_002c:  ldsfld     class linq_relating_to_weak_reference.Test/HandleMessage linq_relating_to_weak_reference.Test::'CS$<>9__CachedAnonymousMethodDelegate1'  
      IL_0031:  stloc.1  
      IL_0032:  ret  
    } // end of method Test::Main  
     

    Code on line 12 shows:

    ldsfld pushes the value of a static field onto the evaluation stack.

    'CS$<>9__CachedAnonymousMethodDelegate1'  is the static field generated by compiler.

    then on line 25 ,

    listener2 is set to the compiler generated delegate.


    Best regards,
    Harry

  • venerdì 13 febbraio 2009 15.12sailing8036 Medaglie utenteMedaglie utenteMedaglie utenteMedaglie utenteMedaglie utente
     Con risposta

    Thank Harry for your correct reply.

    http://stackoverflow.com/questions/535972/lambda-expression-cause-weakreferences-target-cannot-be-gc
    I also got the answer from above。

    The following example show that :

    if Test#create() method don't reference to any Test instance object's properties or methods, then "private static HandleMessage CS$<>9__CachedAnonymousMethodDelegate1" will be created by compiler, like what Jon Skeet has said - That makes it more efficient when you use the same lambda expression multiple times.

    if Test#create() method reference to any Test instance object's properties or methods, like the example below calling this.toString(); then compiler can not create static method to replace the intstance's method logic, so after GC the HandleMessage instance can be collected.

    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();
       
    }
    }

    }

    Best regards,
    Eric