views:

227

answers:

2

I am trying to track down why a string is stored so long in my application, and eating up an excessive amount of memory. I have a Windows Service which runs regularly.

It reads data from a database (in the form of a DataSet) and then does some processing - all managed .NET.

The Windows Service is triggered once every 5 or so minutes, which does some cross-referencing. Each row of the DataSet shouldn't take much more than a second - worst case!

At one stage the Private Bytes > 1.2GB, even though there was no data available to process. There are no global variables, and all processing is done within individual methods.

I took a snapshot and processed with WinDbg. Here are the results:

0:000> !dumpheap -min 85000
 Address       MT     Size
02027f40 00166620   101432 Free
28411000 79330b24 536870936     
48c11000 79333594 226273040     
08411000 79330b24 452546504     
total 4 objects
Statistics:
      MT    Count    TotalSize Class Name
00166620        1       101432      Free
79333594        1    226273040 System.Byte[]
79330b24        2    989417440 System.String
Total 4 objects

And so we want to find the 2 strings that are causing the problem:

0:000> !dumpheap -mt 79330b24  -min 85000
 Address       MT     Size
28411000 79330b24 536870936     
08411000 79330b24 452546504     
total 2 objects
Statistics:
      MT    Count    TotalSize Class Name
79330b24        2    989417440 System.String
Total 2 objects

Now I want to find out where these 2 are located, but when I use !gcroot, it doesn't return any results:

0:000> !gcroot 28411000 
Note: Roots found on stacks may be false positives. Run "!help gcroot" for
more info.
Scan Thread 0 OSTHread 2970
Scan Thread 2 OSTHread 2ab4
Scan Thread 3 OSTHread 12ac
Scan Thread 4 OSTHread 1394
Scan Thread 5 OSTHread 1b78
Scan Thread 8 OSTHread 1364
Scan Thread 9 OSTHread 226c
Scan Thread 10 OSTHread 1694
0:000> !gcroot 08411000 
Note: Roots found on stacks may be false positives. Run "!help gcroot" for
more info.
Scan Thread 0 OSTHread 2970
Scan Thread 2 OSTHread 2ab4
Scan Thread 3 OSTHread 12ac
Scan Thread 4 OSTHread 1394
Scan Thread 5 OSTHread 1b78
Scan Thread 8 OSTHread 1364
Scan Thread 9 OSTHread 226c
Scan Thread 10 OSTHread 1694

I don't understand what I am doing wrong, or why the string's root cannot be found. I have done !do on it, and it just says the strings are unprintable:

0:000> !do 28411000 
Name: System.String
MethodTable: 79330b24
EEClass: 790ed65c
Size: 536870930(0x20000012) bytes
 (C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
String: <String is invalid or too large to print>

Fields:
      MT    Field   Offset                 Type VT     Attr    Value Name
79332d70  4000096        4         System.Int32  0 instance 268435457 m_arrayLength
79332d70  4000097        8         System.Int32  0 instance 226273026 m_stringLength
79331804  4000098        c          System.Char  0 instance       57 m_firstChar
79330b24  4000099       10        System.String  0   shared   static Empty
    >> Domain:Value  00159f38:01021198 <<
79331754  400009a       14        System.Char[]  0   shared   static WhitespaceChars
    >> Domain:Value  00159f38:010217d4 <<

And

0:000> !do 08411000 
Name: System.String
MethodTable: 79330b24
EEClass: 790ed65c
Size: 452546502(0x1af94fc6) bytes
 (C:\WINDOWS\assembly\GAC_32\mscorlib\2.0.0.0__b77a5c561934e089\mscorlib.dll)
String: <String is invalid or too large to print>

Fields:
      MT    Field   Offset                 Type VT     Attr    Value Name
79332d70  4000096        4         System.Int32  0 instance 226273243 m_arrayLength
79332d70  4000097        8         System.Int32  0 instance 226273242 m_stringLength
79331804  4000098        c          System.Char  0 instance       45 m_firstChar
79330b24  4000099       10        System.String  0   shared   static Empty
    >> Domain:Value  00159f38:01021198 <<
79331754  400009a       14        System.Char[]  0   shared   static WhitespaceChars
    >> Domain:Value  00159f38:010217d4 <<

Can someone help please?

--

Update:

!eeheap -gc

Number of GC Heaps: 1
generation 0 starts at 0x01175764
generation 1 starts at 0x011756dc
generation 2 starts at 0x01021000
ephemeral segment allocation context: none
 segment    begin allocated     size
01020000 01021000  0117b770 0x0015a770(1419120)
Large object heap starts at 0x02021000
 segment    begin allocated     size
02020000 02021000  02040d88 0x0001fd88(130440)
28410000 28411000  48411018 0x20000018(536870936)
48c10000 48c11000  563db710 0x0d7ca710(226273040)
08410000 08411000  233a5fc8 0x1af94fc8(452546504)
Total Size  0x488d9be8(1217240040)
------------------------------
GC Heap Size  0x488d9be8(1217240040)

Update 2: I removed the references to XML as this particular program doesn't process XML - my bad.


Update 3:

Here is the results of using psscor2.dll

0:000> !heapstat -inclUnrooted
Heap     Gen0         Gen1         Gen2         LOH
Heap0    32780        68316        1324728      1217845888  

Free space:                                                 Percentage
Heap0    12           67212        59764        101640      SOH:  8% LOH:  0%

Unrooted objects:                                           Percentage
Heap0    2684         1104         757416       1217715448  SOH: 53% LOH: 99%
+3  A: 

EDITED (D'oh, more coffee required) These strings are bigger than 85,000 bytes so they will be residing on the large object heap which is rarely garbage collected and not compacted (leading to fragmentation, especially if you're allocating lots of short lived large objects).

What WinDbg is telling you is correct - these don't have a root and they are garbage, but because they're on the LOH they may not get cleared out quickly (if at all).

You definitely need to re-think how you are processing the XML, look to streaming data in/out rather than loading/creating in memory up-front.

Paolo
So are you saying any object residing on the LOH cannot be found using WinDBG? As I have a 226MB byte array that I'm not sure of either?
Dominic Zukiewicz
Re-edited my answer as I realised I was talking nonsense about the LOH not being collected (it's not *compacted*). WinDBG is finding the objects on your LOH fine as far as I can see, the issue is that garbage objects are not being collected quickly because they are on the LOH, your latest update shows that in stark relief.
Paolo
So when we say unrooted, we just mean objects that are no longer referenced, but in my case they haven't been garbage collected?
Dominic Zukiewicz
Yes, that's correct
Paolo
A: 

The objects in LOH will be collected only when there is memory pressure. GC would collect objects in LOH only when it is doing a full collection.

The psscor2.dll (extension to debug managed code) has a command

!HeapStat [-inclUnrooted | -iu]

which will dump only valid roots , compared to !eeheap

Naveen