In my ongoing investigation of high CPU usage in our application I have come to the conclusion that it's the GC taking up most of the CPU time. Looking at a memdump of the w3wp process (the app is iis hosted) I found something strange: there was a LOT of 46 byte large string objects on the heap, all of them dead and ready to be collected. Investigating further the following command:
!dumpheap -mt 00007ff9bd160e08 -dead -min 2e -max 2e
Which dumps out all dead string objects of size 46 told me that I currently have 260000 dead strings of that size. Looking inside a few of them quickly revealed that all of them comes from the same place, a helper method that creats keys for our cache:
private static string Key(Guid ssoId)
{
return string.Format("BasicUser:{0}-Brand:{1}", ssoId, MemBag.Client.BrandEnum);
}
The 46byte string is "BasicUser:" nothing more, at least as far as I can see:
0:000> !do 000000f1d48d49a0
Name: System.String
MethodTable: 00007ff9bd160e08
EEClass: 00007ff9bca84860
Size: 46(0x2e) bytes
File: C:\Windows\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll
String: BasicUser:
Fields:
MT Field Offset Type VT Attr Value Name
00007ff9bd163980 40000ab 8 System.Int32 1 instance 10 m_stringLength
00007ff9bd1620f0 40000ac c System.Char 1 instance 42 m_firstChar
00007ff9bd160e08 40000ad 18 System.String 0 shared static Empty
>> Domain:Value 000000ee12ced200:NotInit 000000ee138a4440:NotInit <<
All those allocations seems to be causing not just gen0 collections but a LOT of full collections as well, around 80-90% collections are full collections!
The method does get called a lot, but it makes no sense that it puts so much pressure on the GC! What is going on here? Is the method implemented in a bad way? how come is the string on the heap "BasicUser:" ? where is the rest of the string? even with empty params the "Brand" part is still missing.
I am very puzzled about this issue and really need some input here, any help is very appreciated!
Update
It seems like its always 10 chars long, I changed the code to:
return string.Format("BasicUsr:{0}-Brand:{1}", ssoId, MemBag.Client.BrandEnum);
Changing User -> Usr, the result is that I now see strings like this on the heap :"BasicUsr:b" It seems to be the 10 first chars always, am I dumping the string out wrong?
Update 2
I got that chance to get a 1 minute perfview capture from the live server, this is what I got and if I am reading it correct we alloc huge big amounts of data which is the reason for the high mem pressure, almost every collection is gen2