64

I have a class, and I want to inspect its fields and report eventually how many bytes each field takes. I assume all fields are of type Int32, byte, etc.

How can I find out easily how many bytes does the field take?

I need something like:

Int32 a;
// int a_size = a.GetSizeInBytes;
// a_size should be 4
Shaun Luttin
  • 133,272
  • 81
  • 405
  • 467

9 Answers9

106

You can't, basically. It will depend on padding, which may well be based on the CLR version you're using and the processor etc. It's easier to work out the total size of an object, assuming it has no references to other objects: create a big array, use GC.GetTotalMemory for a base point, fill the array with references to new instances of your type, and then call GetTotalMemory again. Take one value away from the other, and divide by the number of instances. You should probably create a single instance beforehand to make sure that no new JITted code contributes to the number. Yes, it's as hacky as it sounds - but I've used it to good effect before now.

Just yesterday I was thinking it would be a good idea to write a little helper class for this. Let me know if you'd be interested.

EDIT: There are two other suggestions, and I'd like to address them both.

Firstly, the sizeof operator: this only shows how much space the type takes up in the abstract, with no padding applied round it. (It includes padding within a structure, but not padding applied to a variable of that type within another type.)

Next, Marshal.SizeOf: this only shows the unmanaged size after marshalling, not the actual size in memory. As the documentation explicitly states:

The size returned is the actually the size of the unmanaged type. The unmanaged and managed sizes of an object can differ. For character types, the size is affected by the CharSet value applied to that class.

And again, padding can make a difference.

Just to clarify what I mean about padding being relevant, consider these two classes:

class FourBytes { byte a, b, c, d; }
class FiveBytes { byte a, b, c, d, e; }

On my x86 box, an instance of FourBytes takes 12 bytes (including overhead). An instance of FiveBytes takes 16 bytes. The only difference is the "e" variable - so does that take 4 bytes? Well, sort of... and sort of not. Fairly obviously, you could remove any single variable from FiveBytes to get the size back down to 12 bytes, but that doesn't mean that each of the variables takes up 4 bytes (think about removing all of them!). The cost of a single variable just isn't a concept which makes a lot of sense here.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 6
    @jon - I'd be interested in your helper class... available? – gap May 30 '12 at 14:40
  • @gap: No, I never wrote the helper class, I'm afraid. – Jon Skeet May 30 '12 at 14:44
  • Sorry, I duplicated the question in different words. For furture reference: http://stackoverflow.com/questions/11126375/how-do-i-determine-how-big-a-data-type-is-in-net. – Joshua Belden Jun 20 '12 at 20:45
  • Are there any new developments on this answer? Surely there is a more pretty way than relying on the garbage collector. Also, what about the `ldelema`. If you go down to IL level, wouldn't `ldelema` be reliable in determining the size of a given object? (if you took the address of two side-by-side elements in an array of T, then got the difference)... Or for that matter the `sizeof` instruction should do exactly what this question is asking – Earlz Nov 13 '12 at 02:10
  • @Earlz: I explained why `sizeof` isn't ideal in the answer. Note that my GC-based answer is really to determine the size of an *object* rather than the size of a *field*. – Jon Skeet Nov 13 '12 at 06:47
  • @JonSkeet I mean that `sizeof` as a C# construct acts completely differently from the `sizeof` instruction in IL – Earlz Nov 13 '12 at 14:43
  • @JonSkeet see [my answer](http://stackoverflow.com/a/13372626/69742). It's completely possible to get this information if you drop down to IL level – Earlz Nov 14 '12 at 03:35
17

Depending on the needs of the questionee, Marshal.SizeOf might or might not give you what you want. (Edited after Jon Skeet posted his answer).

using System;
using System.Runtime.InteropServices;

public class MyClass
{
    public static void Main()
    {
        Int32 a = 10;
        Console.WriteLine(Marshal.SizeOf(a));
        Console.ReadLine();
    }
}

Note that, as jkersch says, sizeof can be used, but unfortunately only with value types. If you need the size of a class, Marshal.SizeOf is the way to go.

Jon Skeet has laid out why neither sizeof nor Marshal.SizeOf is perfect. I guess the questionee needs to decide wether either is acceptable to his problem.

Lasse V. Karlsen
  • 380,855
  • 102
  • 628
  • 825
  • The question says "I want to inspect it's fields, and report eventually how much bytes does each field take. I assume all fields are of types as Int32, byte etc." so I don't think Jon Skeet was thinking of class overhead. – Windows programmer Oct 16 '08 at 06:49
  • It's not the class overhead - it's that after marshalling, an object can look very different to how it looks in memory. Marshalling is black magic as far as I'm concerned... but the marshalled size may well be different to the size in memory. I'll edit my answer. – Jon Skeet Oct 16 '08 at 07:14
  • I guess only the original poster can determine wether either is acceptable, or not. Revised my answer and wikied it. – Lasse V. Karlsen Oct 16 '08 at 08:50
12

From Jon Skeets recipe in his answer I tried to make the helper class he was refering to. Suggestions for improvements are welcome.

public class MeasureSize<T>
{
    private readonly Func<T> _generator;
    private const int NumberOfInstances = 10000;
    private readonly T[] _memArray;

    public MeasureSize(Func<T> generator)
    {
        _generator = generator;
        _memArray = new T[NumberOfInstances];
    }

    public long GetByteSize()
    {
        //Make one to make sure it is jitted
        _generator();

        long oldSize = GC.GetTotalMemory(false);
        for(int i=0; i < NumberOfInstances; i++)
        {
            _memArray[i] = _generator();
        }
        long newSize = GC.GetTotalMemory(false);
        return (newSize - oldSize) / NumberOfInstances;
    }
}

Usage:

Should be created with a Func that generates new Instances of T. Make sure the same instance is not returned everytime. E.g. This would be fine:

    public long SizeOfSomeObject()
    {
        var measure = new MeasureSize<SomeObject>(() => new SomeObject());
        return measure.GetByteSize();
    }
  • I'd change 'generator' to the more standard term 'factory'. – redcalx Nov 27 '12 at 11:24
  • The approach doesn't take into account the initial memory allocation for `_memArray`. I.e., it makes sense to measure `oldSize` _before_ `_memArray = new T[NumberOfInstances];` – Alexey Khoroshikh May 28 '13 at 07:11
  • 4
    @Alexey But you do not want to measure _memArray ! It is just a helper array to contain the stuff you want to measure i.e. the size of T. That is the whole point of this class. – Jesper Niedermann May 28 '13 at 11:20
  • Yes, @Jesper, you're definitely right theoretically, meaning the "pure" class instance size. However, if talking about real world examples (e.g. "how many instances could be loaded into the free memory", the issue I had) one has to take the reference to this instance into account. So my estimation is just more useful despite its theoretical incorectness. Obviously, I had to include this in the previous comment. Sorry for misunderstanding I brought. – Alexey Khoroshikh Jun 05 '13 at 08:46
  • 1
    @Alexey OK I see your point. But I guess the arrays size would be miniscule compared to the content of the array. Would it not ? Besides the original question was more about measuring the size of a single object. But of course feel free to change the code as needed :) – Jesper Niedermann Jun 06 '13 at 20:50
  • I suggest you pass `true` to GetTotalMemory, to (try and) force garbage collection before measuring in order to get the most accurate possible value for currently allocated memory. – yoyo Dec 21 '16 at 01:37
  • @yoyo to my knowledge passing true to GetTotalMemory would be a bad idea. You suddenly make the whole thing (more) imprecise and non-determinististic because the GC can be partial. I would assume that using GetTotalMemory(false) makes it deterministic if the process is one-threaded, and T is a valuetype this is because I do not use the heap between the two calls thus GC cannot occur between the calls. In other cases it might require a couple of runs to be absolutely sure that you get the correct result. Because GC can occur then. Not absolutely sure. – Jesper Niedermann Dec 26 '16 at 20:06
  • @JesperNiedermann you are correct to raise the caution, but I would say whether to pass true or false depends on your environment and your intent. I was trying to measure allocations as accurately as possible, and passing true provided more accurate and granular results. At least on Windows using .NET. Under Unity and Mono, GC collection is too slow, so passing true makes performance unusable. Try both? – yoyo Dec 28 '16 at 02:08
5

It can be done indirectly, without considering the alignment. The number of bytes that reference type instance is equal service fields size + type fields size. Service fields(in 32x takes 4 bytes each, 64x 8 bytes):

  1. Sysblockindex
  2. Pointer to methods table
  3. +Optional(only for arrays) array size

So, for class without any fileds, his instance takes 8 bytes on 32x machine. If it is class with one field, reference on the same class instance, so, this class takes(64x):

Sysblockindex + pMthdTable + reference on class = 8 + 8 + 8 = 24 bytes

If it is value type, it does not have any instance fields, therefore in takes only his fileds size. For example if we have struct with one int field, then on 32x machine it takes only 4 bytes memory.

Sergey Popov
  • 538
  • 5
  • 14
5

I had to boil this down all the way to IL level, but I finally got this functionality into C# with a very tiny library.

You can get it (BSD licensed) at bitbucket

Example code:

using Earlz.BareMetal;

...
Console.WriteLine(BareMetal.SizeOf<int>()); //returns 4 everywhere I've tested
Console.WriteLine(BareMetal.SizeOf<string>()); //returns 8 on 64-bit platforms and 4 on 32-bit
Console.WriteLine(BareMetal.SizeOf<Foo>()); //returns 16 in some places, 24 in others. Varies by platform and framework version

...

struct Foo
{
  int a, b;
  byte c;
  object foo;
}

Basically, what I did was write a quick class-method wrapper around the sizeof IL instruction. This instruction will get the raw amount of memory a reference to an object will use. For instance, if you had an array of T, then the sizeof instruction would tell you how many bytes apart each array element is.

This is extremely different from C#'s sizeof operator. For one, C# only allows pure value types because it's not really possible to get the size of anything else in a static manner. In contrast, the sizeof instruction works at a runtime level. So, however much memory a reference to a type would use during this particular instance would be returned.

You can see some more info and a bit more in-depth sample code at my blog

Earlz
  • 62,085
  • 98
  • 303
  • 499
  • That doesn't actually tell you how much space a field will take up though. That's contextual. For example, what's the result for `byte`? It will depend on whether there are other fields it can fit in with, in terms of padding. – Jon Skeet Nov 14 '12 at 09:15
  • 2
    @JonSkeet of course, but it will work on structs as well. There basically is no possible way to get "what will `byte` be in every single context" It's different in different contexts. But, this will help I'm sure. (if all else fails, you can add fields to a struct one field at a time taking a measure with this to determine the size of each field) – Earlz Nov 14 '12 at 18:03
  • You can't add the structs one at a time, because then you get into padding and "minimum size" issues - for example, a class with a single `int` field takes no more space than a class with no fields at all. Then adding another `int` field will make it take 4 (or 8) more bytes. That doesn't mean there's anything special about the second field - removing the first field would do just as well as removing the second field. Fundamentally I'm not convinced the field size is *really* useful to the OP anyway. I know it's what was requested, but I think it's very rarely genuinely useful. – Jon Skeet Nov 14 '12 at 18:08
  • @JonSkeet well, the original reason for me creating this was to determine the size of arrays containing structs in raw bytes as a way to avoid the large object heap. Beyond that, you're correct, I can't see any good use for it unless you're doing some weird pointer arithmetic or some other stuff that's frowned upon in C# – Earlz Jan 24 '13 at 16:14
  • This library could be useful for diagnostic/benchmarking/profiling purposes. For instance, I have written several generic collection classes. These classes could provide a "TotalSize" property if they had access to the true size of T and to the true size of their own class. And while there is still no "sizeof" for a heap object, I can measure the size of a collection class empirically and then hard-coded as a constant for this purpose (actually I just rely on the known padding rules and the fact that an object header is 2 words [8/16 bytes] or 3-4 words for an array.) – Qwertie Apr 08 '13 at 23:12
  • The only thing that stops me from using it is the fact that it's klunky to distribute a separate DLL for one minor diagnostic feature :( – Qwertie Apr 08 '13 at 23:15
  • 1
    @Qwertie unfortunately it's not possible otherwise :( The reflection APIs don't expose the sizeof opcode for structs. If you want though, you can use something like [ILMerge](http://www.codeproject.com/Articles/9364/Merging-NET-assemblies-using-ILMerge) as a post-build step to keep from having to distribute a separate assembly – Earlz Apr 09 '13 at 02:05
  • 1
    @Qwertie It's trivial to implement simple `IL` using `DynamicMethod`; You don't need a whole separate library/dll. For example, check [this answer](/a/47951125/147511), which also happens to provide another approach to the `ValueType`—or more interestingly, "Formatted Class"—*in-situ* size question. In that answer, I give a general purpose ***managed pointer*** subtraction function in `IL`. It returns a byte-offset and thus supports use with disjoint types, so it can also measure the size of any struct given something that's known to be `pack=1` adjacent. – Glenn Slayden Dec 23 '17 at 22:51
  • @Earlz Re: "*...reason for... creating this was to determine the size of arrays containing structs... as a way to avoid the [LOH]...*" OMG me too! I now wonder, given that **.NET** has sealed up this area of "runtime struct layout" so thoroughly and well, whether this precise use-case—the one that we both exactly hit upon—is the only one they didn't happen to foresee. – Glenn Slayden Dec 23 '17 at 22:57
  • @Earlz Ok, so I made a `DynamicMethod` version of your `Opcodes.SizeOf` idea and the problem is that the `sizeof` IL opcode fails for (non-blittable) structs (and 'formatted types') which contain managed fields. The error is "CLR has detected an Invalid program." That stricter interpretation is maybe leftover from prior to the advent of the formatted class concept in .NET 4.0. I was hoping to have an alternative to my managed pointer subtraction approach, but alas, it appears the functionality available from the `sizeof` opcode is a proper subset of the [method I outlined](/a/47951125/147511). – Glenn Slayden Dec 24 '17 at 00:32
  • Another update: although my managed pointer subtraction doesn't *crash* with non-blittable structs, it only measures the stored handle size itself, always returning `IntPtr.Size`. From [here](/q/1128315/find-size-of-object-instance-in-bytes-in-c-sharp/1128674#comment45853923_1128674) it appears that the only way to get the actual layout size of a managed class is via `Marshal.ReadInt32(typeof(T).​TypeHandle.Value, 4)` At this point, however, note that our "arrays containing structs" scenario is moot, so it's truly unclear if there's any legitimate use for this specific piece of information. – Glenn Slayden Dec 24 '17 at 01:31
1

if you have the type, use the sizeof operator. it will return the type`s size in byte. e.g.

Console.WriteLine(sizeof(int));

will output:

4

Joachim Kerschbaumer
  • 9,695
  • 7
  • 49
  • 84
  • MSDN:Although you can use the SizeOf method, the value returned by this method is not always the same as the value returned by sizeof. Marshal.SizeOf returns the size after the type has been marshaled whereas sizeof returns thesize as it has been allocated by the CLR, including any padding. – Philip Fourie Oct 16 '08 at 06:46
  • Link to MSDN: ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.en/dv_csref/html/c548592c-677c-4f40-a4ce-e613f7529141.htm – Philip Fourie Oct 16 '08 at 06:47
0

You can use method overloading as a trick to determine the field size:

public static int FieldSize(int Field) { return sizeof(int); }
public static int FieldSize(bool Field) { return sizeof(bool); }
public static int FieldSize(SomeStructType Field) { return sizeof(SomeStructType); }
dakab
  • 5,379
  • 9
  • 43
  • 67
0

Simplest way is: int size = *((int*)type.TypeHandle.Value + 1)

I know this is implementation detail but GC relies on it and it needs to be as close to start of the methodtable for efficiency plus taking into consideration how GC code complex is nobody will dare to change it in future. In fact it works for every minor/major versions of .net framework+.net core. (Currently unable to test for 1.0)
If you want more reliable way, emit a struct in a dynamic assembly with [StructLayout(LayoutKind.Auto)] with exact same fields in same order, take its size with sizeof IL instruction. You may want to emit a static method within struct which simply returns this value. Then add 2*IntPtr.Size for object header. This should give you exact value.
But if your class derives from another class, you need to find each size of base class seperatly and add them + 2*Inptr.Size again for header. You can do this by getting fields with BindingFlags.DeclaredOnly flag.

TakeMeAsAGuest
  • 957
  • 6
  • 11
0

System.Runtime.CompilerServices.Unsafe

Use System.Runtime.CompilerServices.Unsafe.SizeOf<T>() where T: unmanaged

(when not running in .NET Core you need to install that NuGet package)

Documentation states:

Returns the size of an object of the given type parameter.

It seems to use the sizeof IL-instruction just as Earlz solution does as well. (source)

The unmanaged constraint is new in C# 7.3

Bruno Zell
  • 7,761
  • 5
  • 38
  • 46