We had a dump where we got a .NET OutofMemory exception. The ASP.NET folks looked at the dump and saw a large number of rooted TDS objects. I wasn't sure where those were coming from, so pulled up the dump to evaluate this.
0:033> !dumpheap -stat...0x65400574 11,959 1,339,408 System.Data.SqlClient.SqlConnectionString0x654049c0 11,959 1,482,916 System.Data.SqlClient.TdsParser0x65405fdc 11,958 1,530,624 System.Data.SqlClient._SqlMetaData0x65404744 11,959 1,674,260 System.Data.SqlClient.SqlInternalConnectionTds0x79101fe4 36,452 2,041,312 System.Collections.Hashtable0x65404dec 11,959 2,248,292 System.Data.SqlClient.TdsParserStateObject0x7910be50 206,356 2,476,272 System.Boolean0x65400808 233,910 4,678,200 System.Data.Common.NameValuePermission0x65400b64 221,950 5,326,800 System.Data.Common.NameValuePair0x7912d8f8 257,690 5,741,720 System.Object[]0x000df590 175 10,314,064 Free0x7912d9bc 36,461 13,194,048 System.Collections.Hashtable+bucket[]0x790fd8c4 493,301 34,033,216 System.String0x7912dae8 48,478 192,431,300 System.Byte[]Total 1,994,862 objects, Total size: 290,427,832
Notice any pattern? 11959 seems to be pretty repetitive. I especially don't understand why we have almost 12000 internal connections unless....
Yep - customer is not pooling!!
First, dump out the method table for the System.Data.SqlClient.SqlConnectionString
0:033> !dumpheap -mt 0x65400574
This produces a ridiculously large (11,959 members!) list of SqlConnectionStrings. So, let's dump out one at random:
0:033> !do 0x2e016064 Name: System.Data.SqlClient.SqlConnectionStringMethodTable: 65400574EEClass: 6544c6c0Size: 112(0x70) bytesGC Generation: 2 (C:\WINDOWS\assembly\GAC_32\System.Data\2.0.0.0__b77a5c561934e089\System.Data.dll)Fields: MT Field Offset Type VT Attr Value Name790fd8c4 4000bce 4 System.String 0 instance 2e015e50 _usersConnectionString
And then keep drilling into him...
0:033> !do 2e015e50Name: System.String
String: Data Source=cpsrv;Initial Catalog=Changepoint;Integrated Security=False;User ID=xxxxx;Password=yyyyy;Enlist=False;Pooling=False;Asynchronous Processing=True;Application Name=SqlQueryNotificationService-ac370dde-d9c4-4cbe-9d82-7d7a8df159c6
Let's do another one, too
0:033> !do 0x2c5b9aa8 Name: System.Data.SqlClient.SqlConnectionStringMethodTable: 65400574EEClass: 6544c6c0Size: 112(0x70) bytesGC Generation: 2 (C:\WINDOWS\assembly\GAC_32\System.Data\2.0.0.0__b77a5c561934e089\System.Data.dll)Fields: MT Field Offset Type VT Attr Value Name790fd8c4 4000bce 4 System.String 0 instance 2c5b9894 _usersConnectionString
0:033> !do 2c5b9894Name: System.StringMethodTable: 790fd8c4EEClass: 790fd824Size: 530(0x212) bytesGC Generation: 2 (C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)String: Data Source=cpsrv;Initial Catalog=Changepoint;Integrated Security=False;User ID=xxxx;Password=yyyy;Enlist=False;Pooling=False;Asynchronous Processing=True;Application Name=SqlQueryNotificationService-78b81d9b-ecd9-400d-8435-6e745d449c96
Note that in both cases, the connection string has Pooling=False and an application name that has a GUID. Either one of these will prevent good use of connection pooling.
The customer needs to turn pooling on, plus eliminate the GUID from the Application Name.