Jaa


ASP.NET Tips: Looking at the finalization queue

So in a previous post, we talked about Understanding when to use a Finalizer in your .NET class so now lets take a look at what the Finalize queue looks like and how to tell if things are bad.

The command we use is !finalizequeue in sos:

 0:010> !finalizequeue
SyncBlock to be cleaned up: 86
----------------------------------
MTA interfaces to be released: 0
Total STA interfaces to be released: 0
----------------------------------
------------------------------
Heap 0
generation 0 has 6 finalizable objects (0x17a4e13c->0x17a4e154)
generation 1 has 0 finalizable objects (0x17a4e13c->0x17a4e13c)
generation 2 has 77 finalizable objects (0x17a4e008->0x17a4e13c)
Ready for finalization 0 objects (0x17a4e154->0x17a4e154) - Freachable queue
------------------------------
Heap 1
generation 0 has 0 finalizable objects (0x17ad591c->0x17ad591c)
generation 1 has 9 finalizable objects (0x17ad58f8->0x17ad591c)
generation 2 has 100 finalizable objects (0x17ad5768->0x17ad58f8)
Ready for finalization 2 objects (0x17ad591c->0x17ad5924) - Freachable queue
------------------------------
Heap 2
generation 0 has 2 finalizable objects (0x17a70920->0x17a70928)
generation 1 has 18 finalizable objects (0x17a708d8->0x17a70920)
generation 2 has 306 finalizable objects (0x17a70410->0x17a708d8)
Ready for finalization 11 objects (0x17a70928->0x17a70954) - Freachable queue
------------------------------
Heap 3
generation 0 has 0 finalizable objects (0x002295f4->0x002295f4)
generation 1 has 6 finalizable objects (0x002295dc->0x002295f4)
generation 2 has 155 finalizable objects (0x00229370->0x002295dc)
Ready for finalization 11 objects (0x002295f4->0x00229620) - Freachable queue
All Finalizable Objects Statistics:
        MT      Count     TotalSize Class Name
0x166cea68          2            24 System.Data.OleDb.OleDbConnection/OleDbWrapper
0x79ba41f0          2            32 System.Threading.AutoResetEvent
0x79ba58b8          3            36 System.Security.Cryptography.RNGCryptoServiceProvider
0x160d40a8          3            48 System.Web.Security.FileSecurityDescriptorWrapper
0x19b3a7cc          1            56 System.Web.RegularExpressions.DataBindRegex
0x19b39434          1            56 System.Web.RegularExpressions.RunatServerRegex
0x19b393ac          1            56 System.Web.RegularExpressions.ServerTagsRegex
0x19b39324          1            56 System.Web.RegularExpressions.LTRegex
0x19b3929c          1            56 System.Web.RegularExpressions.GTRegex
0x19b39214          1            56 System.Web.RegularExpressions.TextRegex
0x19b3918c          1            56 System.Web.RegularExpressions.IncludeRegex
0x19b39104          1            56 System.Web.RegularExpressions.CommentRegex
0x19b3907c          1            56 System.Web.RegularExpressions.DatabindExprRegex
0x19b38ff4          1            56 System.Web.RegularExpressions.AspExprRegex
0x19b38f6c          1            56 System.Web.RegularExpressions.AspCodeRegex
0x19b38ee4          1            56 System.Web.RegularExpressions.EndTagRegex
0x19b38e5c          1            56 System.Web.RegularExpressions.DirectiveRegex
0x19b38dd4          1            56 System.Web.RegularExpressions.TagRegex
0x16c71ce8          1            56 System.Data.OleDb.DBPropSet
0x160da80c          1            56 System.Web.RegularExpressions.SimpleDirectiveRegex
0x79ba3e34          2            80 System.Runtime.Remoting.Lifetime.LeaseManager
0x16c72758          8            96 System.Data.OleDb.PropertyIDSetWrapper
0x160dad60          4           112 System.Web.Compilation.CompilationMutex
0x16c71434          2           128 System.Data.OleDb.DBBindings
0x79b97824          3           180 System.Runtime.Remoting.Contexts.Context
0x79b89c4c          4           208 System.IO.StreamWriter
0x15f490fc         11           220 System.Web.NativeDirMonCompletion
0x166cbe18          2           256 System.Data.OleDb.OleDbDataReader
0x16c75b74          5           280 System.Data.OleDb.OleDbDataAdapter
0x79b7ee18          5           320 System.IO.FileStream
0x79b7d004         20           320 System.Threading.ManualResetEvent
0x79ba438c         14           336 System.Threading.Timer
0x166cad9c          5           400 System.Data.OleDb.OleDbCommand
0x166c9d44          5           440 System.Data.OleDb.OleDbConnection
0x79b7df2c         17           476 Microsoft.Win32.RegistryKey
0x79ba8544         23           644 System.Security.Principal.WindowsIdentity
0x163f9734         25           700 Microsoft.VisualBasic.CompilerServices.ProjectData
0x163fa3e4         12           912 System.Data.DataSet
0x79b6e18c         28         1,792 System.Threading.Thread
0x163fbad0         13         2,912 System.Data.DataTable
0x79b7e8b0        216         3,456 System.WeakReference
0x16c73514         76         3,648 System.Data.OleDb.NativeDBType
0x160d6cd4         93         5,208 System.Text.RegularExpressions.Regex
0x163fcdd0         88         9,240 System.Data.DataColumn
Total 707 objects, Total size: 34,040

From this output we can see all of the objects that have been created that have a finalize method.  If you call dispose on the object and it ends up calling GC.SuppressFinalize, then it will still be in this list, but will be able to be collected like an object that does not need to be finalized.  So other then those objects, these are all of the objects that will need to be finalized.  After each heap you will see a line about Ready for finalization… this show how many of the objects will need to run on the finalize thread currently.  If you use the sos that comes with the debugger package (works only with 1.x version) you can add the -detail switch and see the Freachable Queue which is all of the objects that are ready to be finalized (GC.SuppressFinalize wasn't called but the object is ready to be cleaned up) as seen here:

 Freachable Queue Statistics:
        MT      Count     TotalSize Class Name
0x79b7d004          2            32 System.Threading.ManualResetEvent
0x16c72758          3            36 System.Data.OleDb.PropertyIDSetWrapper
0x79b89c4c          1            52 System.IO.StreamWriter
0x79ba8544          2            56 System.Security.Principal.WindowsIdentity
0x79b7df2c          2            56 Microsoft.Win32.RegistryKey
0x16c71ce8          1            56 System.Data.OleDb.DBPropSet
0x16c71434          2           128 System.Data.OleDb.DBBindings
0x79b7e8b0         10           160 System.WeakReference
0x166cbe18          2           256 System.Data.OleDb.OleDbDataReader
0x163fcdd0          3           396 System.Data.DataColumn
Total 28 objects, Total size: 1,228

When you see the !finalizequeue list showing thousands of objects, that is usually a sign that too many objects have a finalizer.  The best way to troubleshoot this is to look for objects that are not part of the framework and then make sure that they follow the rules we discussed earlier

There are other things that can go wrong with the finalizer but we will discuss them later.  Some are already discussed on Tess’s blog.

A useful link about writing faster code is here.  This also talks about calling GC.SuppressFinalize.  There are also some very useful information in the following posts:

kick it on DotNetKicks.com

Comments

  • Anonymous
    April 28, 2008
    You've been kicked (a good thing) - Trackback from DotNetKicks.com

  • Anonymous
    April 28, 2008
    Want to see what objects you have added a finalizer too, follow the information here .

  • Anonymous
    November 30, 2009
    Hi there; I think it's a mistake to suggest that GC.SuppressFinalize will 'remove' the object from the !finalizequeue in SOS - that you still see Systme.Data.DataTables (which call GC.SuppressFinalize) in their constructors is perfect evidence of this. Calling SuppressFinalize will prevent the promotion to FReachable and allow the object to be reclaimed on the first GC pass (just as non-finalizable objects would) but it will still list under this output. As such, this command in and of itself isn't very useful: a command in the public SOS that works for 2.x assemblies that shows FReachable or the Finalization Queue (where the bit in the header for finalization-requires is still set to ON) would help. [1]- http://www.devnewsgroups.net/dotnetframework/t19821-finalize-queue-windbg-sos.aspx

  • Anonymous
    December 01, 2009
    Excellent point Nariman.  I am updating the post to reflect that difference.