Lambda Expression cause weakreference's target cannot be GC?
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]
Réponses
- 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- Proposé comme réponseStephen Cleary vendredi 13 février 2009 14:32
- Marqué comme réponseHarry ZhuMSFT, Modérateurlundi 16 février 2009 01:07
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- Marqué comme réponseHarry ZhuMSFT, Modérateurlundi 16 février 2009 01:07
Toutes les réponses
- 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- Proposé comme réponseStephen Cleary vendredi 13 février 2009 14:32
- Marqué comme réponseHarry ZhuMSFT, Modérateurlundi 16 février 2009 01:07
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- Marqué comme réponseHarry ZhuMSFT, Modérateurlundi 16 février 2009 01:07

