开发者

Delegate variables not garbage collected

Recently discovered that the variables inside ToGadget, and presumably the delegate as well, weren't getting garbage collected. Can anyone see why .NET holds a reference to this? Seems that the delegate and all would be marked for garbage collection after Foo ends. Literally saw Billions in memory after dumping the heap.

Note: 'result.Things' is a List<Gadget> () and Converter is a System delegate.


        public Blah Foo()
        {
                var result = new Blah();
                result.Things = this.Things.ConvertAll((new Converter(ToGadget)));
       开发者_如何学运维         return result;
        }
        .................
        public static Gadget ToGadget(Widget w)
        {
            return new Gadget(w);
        }

Update: changing the 'ConvertAll' to this cleans up the delegates and corresponding object references. This suggests to me that either List<> ConvertAll is somehow holding on to the delegate or I don't understand how these things are garbage collected.


            foreach (var t in this.Things)
            {
                result.Things.Add(ToGadget(t));         
            }


Use a memory profiler.

You can ask on StackOverflow all day and get a bunch of educated guesses, or you can slap a memory profiler on your application and immediately see what is rooted and what is garbage. There are tools available that are built specifically to solve your exact problem quickly and easily. Use them!


There is one major flaw in your question, which may be the cause of confusion:

Seems that the delegate and all would be marked for garbage collection after Foo ends.

The CLR doesn't "mark items" for collection at the end of a routine. Rather, once that routine ends, there is no longer an (active) reference to any of the items referenced in your delegate. At that point, they are what is refered to as "unrooted".

Later, when the CLR determines that there is a certain amount of memory pressure, the garbage collector will execute. It will search through and find all unrooted elements, and potentially collect them.

The important distinction here is that the timing is not something that can be predicted. The objects may never be collected until your program ends, or they may get collected right away. It's up to the system to determine when it will collect. This doesn't happen when Foo ends - but rather at some unknown amount of time after Foo ends.


Edit:

This is actually directly addressing your question, btw. You can see if this is the issue by forcing a garbage collection. Just add, after your call to Foo, a call to:

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

Then do your checking of the CLR's heap. At this point, if you're still getting objects in the heap, it's because the objects are still being rooted by something. Your simplified example doesn't show this happening, but as this is a very simplified example, it's difficult to determine where this would happen. (Note: I don't recommend keeping this in your code, if this is the case. Calling GC.Collect() manually is almost always a bad idea...)


It looks like your function is set up to return the new Blah(). Is it actually being returned in your code? I see in the piece you posted that it is not. If that is the case, then the new Blah() would have a scope outside of Foo and it may be the calling function that is actually holding the references in scope. Also, you're creating new Gadget() as well. Depending on how many Blahs to Gadgets you have, you could be exponentially filling your memory as the Gadgets will be scoped with the Blahs which are then held in scope beyond Foo.

Whether I'm right or wrong, this possibility was kinda funny to type.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜