Getting the size of a field in bytes with C#
Asked Answered
T

9

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
Tackett answered 16/10, 2008 at 6:31 Comment(0)
A
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.

Alexandrina answered 16/10, 2008 at 6:39 Comment(7)
@jon - I'd be interested in your helper class... available?Capelin
@gap: No, I never wrote the helper class, I'm afraid.Alexandrina
Sorry, I duplicated the question in different words. For furture reference: #11126875.Jablonski
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 askingPinkie
@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.Alexandrina
@JonSkeet I mean that sizeof as a C# construct acts completely differently from the sizeof instruction in ILPinkie
@JonSkeet see my answer. It's completely possible to get this information if you drop down to IL levelPinkie
R
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.

Rechaba answered 16/10, 2008 at 6:31 Comment(3)
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.Cowgirl
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.Alexandrina
I guess only the original poster can determine wether either is acceptable, or not. Revised my answer and wikied it.Rechaba
D
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();
    }
Dita answered 25/10, 2012 at 13:9 Comment(8)
I'd change 'generator' to the more standard term 'factory'.Radium
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];Cynthy
@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.Dita
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.Cynthy
@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 :)Dita
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.Sundae
@Sundae 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.Dita
@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?Sundae
A
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.

Ardene answered 25/10, 2012 at 7:39 Comment(0)
P
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

Pinkie answered 14/11, 2012 at 3:33 Comment(11)
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.Alexandrina
@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)Pinkie
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.Alexandrina
@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#Pinkie
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.)Beaudry
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 :(Beaudry
@Beaudry 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 as a post-build step to keep from having to distribute a separate assemblyPinkie
@Beaudry It's trivial to implement simple IL using DynamicMethod; You don't need a whole separate library/dll. For example, check this answer, 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.Ixia
@Pinkie 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.Ixia
@Pinkie 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.Ixia
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 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.Ixia
M
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

Miguelmiguela answered 16/10, 2008 at 6:36 Comment(2)
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.Guard
Link to MSDN: ms-help://MS.VSCC.v90/MS.MSDNQTR.v90.en/dv_csref/html/c548592c-677c-4f40-a4ce-e613f7529141.htmGuard
I
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); }
Irradiance answered 14/11, 2015 at 23:0 Comment(0)
D
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.

Divagate answered 28/3, 2019 at 21:9 Comment(0)
L
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

Lockett answered 7/11, 2019 at 17:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.