Objects added to the Large Object Heap

803 views Asked by At

I'm trying to debug the reason for high CPU usage in our legacy website, and from looking at some analysis in DebugDiag, I suspect that the amount of objects on the LOH, and subsequent GC collections, could be a reason. In one .dbg file we have ~3.5gb on the LOH with the majority of those objects being strings.

I'm aware that for objects to go on the LOH, they must be over 85000 bytes.

What I'm not sure of is if this refers to, for example, a single array. Or can it refer to a large object graph?

What I mean by that is if I have object Foo, that contains n other objects, each containing n objects themselves. With each of these objects containing strings, and the total size of Foo (and all child objects) being greater than 85000 bytes would Foo be placed on the LOH? Or, if somewhere in the Foo object graph there was a single array greater than 85000 bytes would it just be that array that got placed on the LOH?

Thanks.

3

There are 3 answers

1
dotnetstep On BEST ANSWER

You are right that if array is larger than 85000 then it will be consider as LOH not entire object. To explain this here I created example.

   class Program
    {
        static void Main(string[] args)
        {
            Obj o = new Obj();
            o.Allocate(85000);
            Console.WriteLine(System.GC.GetGeneration(o));
            Console.WriteLine(System.GC.GetGeneration(o.items));
            Console.WriteLine(System.GC.GetGeneration(o.items2));
            Console.WriteLine(System.GC.GetGeneration(o.Data));
            Console.ReadLine();           
        }

        class Obj
        {
            public byte[] items = null;

            public byte[] items2 = null;

            public string Data = string.Empty;

            public void Allocate(int i)
            {
                items = new byte[i];
                items2 = new byte[10];
                Data = System.Text.Encoding.UTF8.GetString(items);
            }
        }
    }

Here if you notice that string data. It is also consider as LOH because string is character array. Items2 is not LOH and Items is LOH but actual object o is not LOH.

0
daspek On

Only individual objects greater than 85,000 bytes go into the LOH. An object graph can total more than that but not be in the LOH so long as no individual object crosses the limit. Byte arrays and strings are the most typical culprits.

0
Eniola On

I agree with @daspek and @dotnetstep.

For instance in @dotnetstep's example, o should have a size of 12bytes on a 32bit machine and 24bytes on a 64bit machine.

All the fields defined in the Obj class are reference types and so all Obj stores are pointers to the heap locations where its child elements (in this case arrays) are created.

Now each of these objects are arrays (string being an array of char[]) so they can get placed on the LOH if they exceed the limit of 85000bytes.

The only way a regular object can get placed on the LOH everytime it is created is if it has 21251 or more fields on a 32 bit machine and 10626 or more fields on a 64 bit machine if all the fields are REFERENCE types. For all intents and purposes, that is not usually a practical kind of class create; it will be extremely rare to come across such a class.

In an instance where the contained fields are structs, they are considered to be part of the class's definition. So rather than hold a 4byte heap address, the contents of the struct are part of the class's layout - a good number of fields with large structs may not quickly get one to the limit of 85000bytes but it can still be breached.