What are the Finalizer Queue and Control+ThreadMethodEntry?
Asked Answered
A

3

11

I have a WindowsForms app that appears to leak memory, so I used Redgate's ANTS Memory Profiler to look at the objects I suspect and find that they are only held by objects already on the Finalizer Queue. Great, exactly what is a the Finalizer Queue? Can you point me to the best definition? Can you share any anecdotal advice?

Also, all the root GC objects on the Finalizer Queue are instances of System.Windows.Forms.Control+ThreadMethodEntry objects named "caller". I see that it is involved in multi-thread UI interaction, but I do not know much beyond that. Forgive my apparent laziness and admitted ignorance, but these resources are all buried within a vendor's component. I am talking to the vendor about these issues, but I need some direction to get me up to speed on the conversation. Can you point me to the most useful definition of ThreadMethodEntry too? Any anecdotal advice?

Also, should I even be concerned about these objects on the finalizer queue?

Update: This Red Gate article was helpful.

Aubarta answered 12/8, 2009 at 20:25 Comment(0)
A
22

The finalizer queue holds all objects that have a finalizer method defined. Recall that a finalizer is a means to collect unmanaged resources like handles. When the garbage collector collects garbage, it moves any objects with a finalizer into the finalizer queue. At some point later-- depending on memory pressure, GC heuristics, and the phase of the moon-- when the garbage collector decides to collect these objects, it walks down the queue and runs the finalizers.

Having worked with memory leaks in the past, seeing a bunch your vendor's objects in the finalizer queue could be sloppy code, but it does not indicate a memory leak. Typically, good code will expose a Dispose method that will collect both managed and unmanaged resources, and in doing so, remove themselves from the finalizer queue via GC.SuppressFinalize(). So, if the vendor's objects do implement a Dispose method, and your code doesn't call it, that could lead to a bunch of objects in the finalizer queue.

Have you tried creating a snapshot in ANTS between two points in time and comparing the objects created between them? That may help you identify any managed objects being leaked.

Also, if you want to see if the memory goes away when the finalizers are run, try this just to test with:

System.GC.Collect();
System.GC.WaitForPendingFinalizers(); // this method may block while it runs the finalizers
System.GC.Collect();

I do not recommend running this code normally. You might want to run it if you've just done a ton of work and created lots of garbage. For example, in our app, one of our functions can create about 350 MB of garbage that goes to waste after closing an MDI window. Since this is known to leave lots of garbage, we manually force garbage collection.

Also note that there is a low-level property cache in the base Windows.Forms code that will hold on to the last opened modal dialog. This could be a source of a memory leak. One sure way to get rid of this reference is to force another simple dialog to appear, then run the above GC code.

Autograph answered 12/8, 2009 at 20:47 Comment(3)
Thanks for the great answer, Paul. That is the object reference graph I am talking about, looking at new objects in the second snapshot, after resources should be cleaned up. All the objects in the graph that implement IDisposable have a tool tip that says "Dispose() has been called for this object" but the selected object has no such tooltip.Aubarta
Note about ThreadMethodEntry: I think they're used in any Invoke to the UI thread. Every Control object has a Queue of thread callbacks of type ThreadMethodEntry. A callback dequeues a ThreadMethodEntry and runs it. Each ThreadMethodEntry object has a bunch of internal fields. Examining these fields might help you figure out which of these vendor's objects are invoking. I don't recall if you can get that info from ANTS, but I know you can via WinDbg.dll and sos.dll (managed debugger extensions). Look at the "method" delegate and the "caller" control.Autograph
Also, note that the ThreadMethodEntry objects implement a finalizer, but they don't have a Dispose method. When they complete, they'd get moved to the finalizer queue, too.Autograph
Z
1

The finalizer queue is a queue where the object instances that are not used anymore are waiting to be finalized by the GC. All objects in this queue will be finalized and your memory leaks does probably not come from one of these ones directly. But, one of these objects may not release all its unmanaged resources.

The ThreadMethodEntry class is an implementations of IAsyncResult and instances of this class are typically created when invoking asynchronous operations, like using Invoke to update the UI or using Begin*/End* methods.

Zaporozhye answered 12/8, 2009 at 20:30 Comment(0)
D
0

Here's a good blog post which describes a similar issue. At a more technical level, you could look to using SOS.dll (which the blog post describes) and Sosex.dll to help you work out why these ThreadMethodEntry objects are hanging around in memory. There are commands in these WinDbg extensions that can track down what other objects are referencing a specific object in memory.

Deemphasize answered 12/8, 2009 at 20:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.