Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How many bytes get allocated for 3 Point structs on a 64-bit processor?

Tags:

There is a question:

Given:

struct Point {int x; int y;} var p = new Point[3] 

how many bytes of memory will be allocated in stack and in heap if we use a 64-bit processor?

The correct answer for .Net is 44. Can somebody explain how did this number appear?

As far as I understand, p will occupy 8 bytes in stack for x64.

And we have two values of Int32 per structure, thus p.Length * sizeof(Point) 3 * 8 = 24 bytes in heap for an array.

That will be 32 bytes. What is the rest 12 bytes for in this case?

like image 927
John Smith Avatar asked Aug 29 '16 11:08

John Smith


People also ask

How many bytes can a 64 bit processor handle?

Limits of processors In principle, a 64-bit microprocessor can address 16 EiB (16 × 10246 = 264 = 18,446,744,073,709,551,616 bytes, or about 18.4 exabytes) of memory.

How many bytes are allocated to a struct?

Contrary to what some of the other answers have said, on most systems, in the absence of a pragma or compiler option, the size of the structure will be at least 6 bytes and, on most 32-bit systems, 8 bytes. For 64-bit systems, the size could easily be 16 bytes.

How much memory does a struct take?

In 32 bit processor, it can access 4 bytes at a time which means word size is 4 bytes. Similarly in a 64 bit processor, it can access 8 bytes at a time which means word size is 8 bytes. Structure padding is used to save number of CPU cycles.

How many bytes is a struct pointer in C?

As is the size of the struct. On 32-bit machine sizeof pointer is 32 bits 4 bytes On 64 bit machine it's 8 byte.


2 Answers

Your answer of 44 bytes is probably a confusion referring to an array of 32 bit architecture.

In .Net (32 bit):

  • Every object contains 4 bytes for synchronization (lock (obj)).
  • Every object contains 4 bytes of its type token.
  • Every array contains 4 bytes of its length.

The pointer is 8 bytes as you said.

This with the 24 bytes of the array itself gives you 44 bytes.


However, this is the header layout for 32 bit.

As you can see, the memory layout of the following code:

var p = new Point[3]; p[0] = new Point { x = 1, y = 2 }; p[1] = new Point { x = 3, y = 4 }; p[2] = new Point { x = 5, y = 6 };  var p2 = new Point[3]; p2[0] = new Point { x = 8, y = 8 }; p2[1] = new Point { x = 8, y = 8 }; p2[2] = new Point { x = 8, y = 8 }; 

Will be:

Memory layout

You can see the number values in the memory layout as well.


In 64 bit, each field of the header with its takes 8 byte so that the header length is 24 bytes therefore the length of the entire array is 48 bytes and with the variable pointing to the array: 56 bytes.

64 bit architecture memory layout:

64 bit memory layout


Notes:

  • If your array wasn't rounded up to an 8 byte multiple alignment would take place, but it is so alignment is not required. Example (two 1 sized int arrays):

    Alignment

  • Even though the length field of the header is 8 bytes in 64 bit, it's larger than the maximum array size .NET allows therefore only 4 may be used.

Keep in mind that this is an implementation detail and it might change between implementations/versions of the CLR.

like image 75
Tamir Vered Avatar answered Oct 11 '22 15:10

Tamir Vered


Most of this is purely an implementation detail and could change with the next version of the CLR.

Run the following program as x86 or x64 and you can empirically determine the size of your structure:

struct Point { int x; int y; }  class Program {     const int Size = 100000;      private static void Main(string[] args)     {         object[] array = new object[Size];         long initialMemory = GC.GetTotalMemory(true);         for (int i = 0; i < Size; i++)         {             array[i] = new Point[3];         }         long finalMemory = GC.GetTotalMemory(true);         GC.KeepAlive(array);          long total = finalMemory - initialMemory;         Console.WriteLine("Size of each element: {0:0.000} bytes",                           ((double)total) / Size);      } } 

The code is pretty simple but was shamelessly stolen from Jon Skeet.

If you do run this you will get the following results:

x86: 36 byte x64: 48 byte 

In the current implementation the size of every object is aligned to the pointer size, this means every object in x86 is 4 byte aligned, and under x64 8 byte (this is absolutely possible to change - HotSpot in Java for example aligns everything to 8 byte even under x86).

Arrays in C# are somewhat special with their length: While they do have a 4 byte length field, under x64 they also include 4 byte additional padding (vm/object.h:766 contains the interesting part). This is most likely done to guarantee that the start of the actual fields is always 8 byte aligned under x64 which is required to get good performance when accessing longs/doubles/pointers (the alternative would be to only add the padding for these types and specialize the length computation - unlikely to be worth the additional complexity).

On x86 the object header is 8 byte and the array overhead 4 byte, which gives us 36 byte.

On x64 the object header is 16 byte and the array overhead 8 byte. This gives us 24 + 24 = 48 byte.

For anyone wanting actual proof instead of empirical tests about the header size and alignment you can just go to the actual source: Here is the object definition of the coreclr. Check out the comment starting at line 178 which states:

// The only fields mandated by all objects are //  //     * a pointer to the code:MethodTable at offset 0 //     * a poiner to a code:ObjHeader at a negative offset. This is often zero.  It holds information that //         any addition information that we might need to attach to arbitrary objects.  

You can also look at the actual code to see that those pointers are actual pointers and not DWORDs or anything else.

The code for aligning the object sizes is also in the same file:

#define PTRALIGNCONST (DATA_ALIGNMENT-1)  #ifndef PtrAlign #define PtrAlign(size) \     ((size + PTRALIGNCONST) & (~PTRALIGNCONST)) #endif //!PtrAlign 

DATA_ALIGNMENT is defined as 4 for x86 (vm/i386/cgencpu.h) and ARM (vm/arm/cgencpu.h) and 8 for x64 (vm/amd64/cgencpu.h). The code itself is nothing but a standard optimized "round to next multiple of DATA_ALIGNMENT" assuming data alignment is a power of 2 method.

like image 40
Voo Avatar answered Oct 11 '22 17:10

Voo