Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does an array of classes consume ~20% more memory than array of structs?

I'm making a 2D platformer game and to represent a level I'm using 2D array of tiles which are classes with fields for position, type and various flags. When I change the class key word in the tile class to struct, a loaded map consumes about 20% less memory.

I don't know about rights and wrongs of this action, I just want to know why the difference in memory consumption.

Edit: numbers are 1038 MB with tiles as classes and 845 MB as structs (without most of game data).

like image 262
user1306322 Avatar asked Aug 15 '12 19:08

user1306322


3 Answers

An array of objects is actually an array of references, and the objects are stored on the heap.

That means that there is an overhead of 4 or 8 bytes (depending on x86 or x64) for the reference, and then there is an overhead of 8 or 16 bytes for each object on the heap.

In an array of structs, the struct values are stored directly in the array, so there is no extra overhead.

So, if your data is for example 48 bytes, the extra 12 bytes (in x86 mode) would be an overhead of 20% of the total memory used.

Note that there is also a difference in how they are used. If you move tiles around or send one as a parameter to a method, all the data will be copied if you use a struct, but only the reference is copied if you use a class. If you can keep the struct smaller than 16 bytes, the performance difference is small, but if it's larger you may get faster code by using classes.

like image 61
Guffa Avatar answered Oct 20 '22 12:10

Guffa


Each object has an 8 byte header containing a pointer to the type handle and a syncblock index, whereas structs are allocated inline. In addition, you need to allocate a pointer-sized reference for each reference type variable you're using.

This article goes into the details of how objects are created an laid out at runtime.

like image 26
Lee Avatar answered Oct 20 '22 10:10

Lee


A storage location (variable, parameter, array element, or field) of a struct type holds the values of all its public and private fields, typically concatenated but possibly with some padding. A storage location of class type holds a (possibly null) reference to a heap object (4-8 bytes). A heap object holds 8-16 bytes of overhead plus the contents of all of the public, protected, and private fields held by the object and its ancestors.

If the things you are storing are fixed-size bundles of values, you should most likely use a struct with exposed fields. The notion that structs should be immutable goes back to days when a C# compiler would take a piece of code like:

  readonly Point Pt = new Point(3,4);
  void Yippie() { Pt.X += 5; }

and have Yippie construct a new temporary Point, copy Pt to it, call the X property setter on that temporary instance, and then discard it. Someone figured that the proper way to prevent such nonsense was not to have compilers say "Sorry--you can't call a property setter on a read-only struct variable", but instead to define structures so as to have read-only properties without setters. The proper thing to have done would have been to require that any struct methods or properties which were going to mutate this indicate so in their declaration and forbid the use of such methods or properties on read-only structs.

Wrapping struct fields in properties will impair performance, and I would recommend against it except in cases where one needs to enforce struct invariants. I would also recommend that one avoid declaring structs readonly, since any structures so declared will be copied in full any time any field is accessed.

Incidentally, an important thing to beware of with mutable class types: the state of a mutable class object not only includes the contents of its fields, but also the set of all references that exists to it. In some cases, this can be useful. Often, the only way to prevent major headaches is to have entities which hold references to mutable objects refrain from sharing such references. If making something a class type would require extra work to prevent references from being passed around promiscuously, that's a good sign that the type in question should be a struct.

like image 1
supercat Avatar answered Oct 20 '22 10:10

supercat