Find out the size of a .NET object
Asked Answered
A

8

58

I'm trying to find out how much memory my objects take to see how many of them are ending up on the Large Object Heap (which is anything over 85,000 bytes).

Is it as simple as adding 4 for an int, 8 for a long, 4 (or 8 if you're on 64 bit) for any reference types etc for each object, or are there overheads for methods, properties, etc.?

Anastassia answered 27/11, 2008 at 15:38 Comment(2)
See this question, [Getting the size of a field in bytes with C#][1], and in particular [Jon Skeets answer][2] Basically, it's not as simple as... [1]: #208092 [2]: #208092Anoxia
Related question: sizeof Equivalent for reference typesOutman
B
54

Don't forget that the size of an actual object doesn't include the size of any objects it references.

The only things which are likely to end up on the large object heap are arrays and strings - other objects tends to be relatively small in themselves. Even an object with (say) 10 reference type variables (4 bytes each on x86) and 10 GUIDs (16 bytes each) is only going to take up about 208 bytes (there's a bit of overhead for the type reference and sync block).

Likewise when thinking about the size of an array, don't forget that if the element type is a reference type, then it's only the size of the references that count for the array itself. In other words, even if you've got an array with 20,000 elements, the size of the array object itself will only be just over 80K (on x86) even if it references a lot more data.

Binominal answered 27/11, 2008 at 15:53 Comment(0)
C
37

Please follow these steps to get the size of the object.

  1. Go to Visual Studio 2010 Project Properties → Debug tab → Enable unmanaged code debugging.

  2. Go to the Visual Studio Debug menu → Options and SettingsDebuggingSymbols.

  3. There, enable Microsoft Symbol Server and leave the default (symbols may start a download).

  4. Set the breakpoint in your code, start debugging (F5).

  5. Open DebugWindowsImmediate Window.

  6. Enter .load sos.dll (Son of Strike)

  7. Enter !DumpHeap -type MyClass (the object you want to find the size of)

  8. From the output, locate the address of the object, i.e. (00a8197c)

    Address MT Size 00a8197c 00955124 36

  9. Next, !ObjSize 00a8197c

  10. There you go → sizeof(00a8197c) = 12 (0x48) bytes (MyClass)

Corrinnecorrival answered 19/8, 2011 at 13:55 Comment(2)
Didn't know you could use sos.dll in visual studio. Really useful, thanks. Also might be useful to note that sos.dll won't load if the application is in 64bit mode. See https://mcmap.net/q/28537/-load-sos-extension-for-debuggingErma
Make sure though, that you understand the "drawbacks" of !ObjSize. It will account for cycles in your object graph. For example if you have a dictionary, with values that reference the dictionary itself, each value will have the "ObjSize" of the whole dictionary.Affect
A
11

If you can - Serialize it!

Dim myObjectSize As Long

Dim ms As New IO.MemoryStream
Dim bf As New Runtime.Serialization.Formatters.Binary.BinaryFormatter()
bf.Serialize(ms, myObject)
myObjectSize = ms.Position
Alexandria answered 5/7, 2011 at 9:36 Comment(6)
Unfortunately if you serialize an object then you also serialize all of the objects that it refers toAnastassia
+1 because this is what I actually wanted to find out (not just the size of the references)Simonson
@MatthewSteeples, an object without its components it's a empty object, so if you want the "size" of your container, it should include the sizes of the compoents.Alexandria
@serhio, yes but as far as the LOH is concerned the size of referenced objects doesn't matter. Each individual object is stored in it's own place and I was just trying to determine if there was an easy way to find out if any of our objects were always going on the LOH.Anastassia
Down, Barry Kelly Says Integer = 4, 8 or 2 Bytes But the Code above, ms.Position is showing 54 in the case of Integer. Please Help me to find why it is so.Headway
The size of a serialized object is not necessarily the same as the size of the live in-memory object, so this solution actually measures a different thing.Unravel
G
9

You are getting into an area of advanced .NET debugging. Start with John Robins debugging books.

Use WinDBG with Sos.dll (part of .NET distribution) and Sosex.dll extensions. With these tools you can really see what's happening when your application is running. You will find answers to your above mentioned questions.

(Another recommendation would be to install Shared Source CLI 2.0, aka. Rotor 2, to see what's going on under the hood.)

Ginny answered 28/11, 2008 at 1:46 Comment(0)
B
5

Gomes's method simplified:

  1. Go to Visual Studio (2010) Project Properties* → Debug tab → Enable unmanaged code debugging.

  2. Set the break point in your code, start debugging (F5).

  3. Open DebugWindowsImmediate Window.

  4. Enter .load sos

  5. Enter (replace myObject with the name of your object)

    ? String.Format("{0:x}",Integer.Parse(System.Runtime.InteropServices.GCHandle.InternalAddrOfPinnedObject(System.Runtime.InteropServices.GCHandle.Alloc(myObject).GetHandleValue()).ToString())

       

  6. Use the result as the parameter of !ObjSize

See: SOS.DLL, object Address and Visual Studio debugger Introduction

Example (we are looking for object named tbl):

.load sos
extension C:\Windows\Microsoft.NET\Framework\v4.0.30319\sos.dll loaded
? string.Format("{0:x}",Integer.Parse(System.Runtime.InteropServices.GCHandle.InternalAddrOfPinnedObject(System.Runtime.InteropServices.GCHandle.Alloc(tbl).GetHandleValue()).ToString())-4)
"27ccb18"
!ObjSize 27ccb18
PDB symbol for clr.dll not loaded
sizeof(027ccb18) =       154504 (     0x25b88) bytes (System.Data.DataTable)
Bedard answered 26/3, 2012 at 10:32 Comment(2)
VS2022 and .NET 8 the command '.load sos.dll' fires "CS1525: Invalid expression term" error.Splanchnic
@BHP: In VS 2022 I usually use memory snapshot from Diagnostic Tools and I think that the sos.dll doesn't work since diagnostic tools have been added to VS (2015?).Bedard
A
4

Unless it's a huge valuetype or instance type (i.e. many thousands of fields), the only types you need to worry about are large arrays, or strings. Of course, to figure out the size of an array, you need to know the element size.

.NET (currently) aligns types in much the same way that native compilers align types. Fundamental types have natural alignments that are usually the rounded-up integral power of two closest to their size:

Single, Int32, UInt32 - 4
IntPtr, UIntPtr, pointers, references  - 4 on 32-bit, 8 on 64-bit
Double, Int64, UInt64 - 8
Char, Int16, UInt16   - 2
Byte, SByte           - 1

When assembling a type, the compiler will make sure that all fields of any given type have their starting offset within the instance aligned to a boundary that matches that type - assuming that explicit layout isn't being used.

User-defined types themselves have an alignment, which is calculated as the highest alignment of any of their field types. The type's size is extended if necessary to make the size of the type aligned too.

But of course, all reference types are still only IntPtr.Size in size and alignment, so the size of reference type will not affect arrays of that type.

Note that the CLR may choose, at its discretion, to layout types differently than described above, maybe to increase cache locality or reduce padding required by alignment.

Ask answered 27/11, 2008 at 15:57 Comment(0)
M
1

As an estimate (in 2017) you can debug into your application, set a breakpoint before your dictionary comes to life, take a "Memory Usage Snapshot" (Tab: Memory Usage under Diagnostic Tools), fill your dictionary and get another snapshot.

It is not exact, but it is a good guesstimate.

Motoneuron answered 31/8, 2017 at 10:44 Comment(0)
D
0

In cases like Dictionary<TKey, TValue>, you can hack getting the object size by binary serialization. Here is a sample code:

var dictionary = new DictionaryGenerator().GetSomeLargeDictionary();
var memoryStream = new System.IO.MemoryStream();
var binaryFormatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
binaryFormatter.Serialize(memoryStream, dictionary);
Console.WriteLine($"Size of dictionary: {memoryStream.Position} byte(s)");
Drabbet answered 24/11, 2021 at 10:22 Comment(2)
Unfortunately if you serialize an object then you also serialize all of the objects that it refers to (see https://mcmap.net/q/28530/-find-out-the-size-of-a-net-object)Anastassia
Yeah, that is the point why I needed the information. I misinterpret your question, but maybe the code above will be helpful for someone who will land on your question like I did. :)Drabbet

© 2022 - 2024 — McMap. All rights reserved.