DOD — Unity Memory Best Practices — Part 5

Nitzan Wilnai
3 min readJun 6, 2024

--

Struct vs Class

Basics

The Garbage Collector only works on the heap. A great way to know whether an object is allocated on the heap, and if you should pay special attention to it in regards to memory usage, is if it needs to be allocated using new().

For example:

class Foo
{
}
Foo myFoo = new Foo();

In this example, myFoo is allocated on the heap. That means we need to pay attention when myFoo will be deallocated, because its deallocation will trigger the Garbage Collector.

Another example:

int[] myIntegerArray = new int[1024];

In this example, myIntegerArray is also allocated on the heap. So we also need to worry about when the Garbage Collector will deallocate it.

But, in this example:

Vector3 myVector3 = new Vector3(0.0f, 0.0f, 0.0f);

myVector3 is actually allocated on the stack, not the heap. That means we do not need to worry about its deallocation. We can call new Vector3() every frame and the Garbage Collector will not be triggered.

This is because Vector3 is a struct, not a class, and will therefore be allocated on the stack, not the heap.

Coincidentally, we do not need to call new() to use myVector3:

Vector3 myVector3;
myVector3.x = myVector3.y = myVector3.z = 0.0f;

So a good rule of thumb is:
If you HAVE to call new() to use an object, it is most likely allocated on the heap, and therefore you need to be careful with how you use it so as not to fragment your memory or trigger the garbage collector.

Struct Allocation

You probably learned in the past that the main difference between Struct vs Class is that Structs are allocated on the Stack while Classes are allocated on the Heap.

This is not always true:

  • Classes are always allocated on the Heap.
  • Structs are sometimes allocated on the Stack.
  • In some instances, structs can be allocated on the heap as well.

It is really important to understand when structs are allocated on the stack, and when they are allocated on the heap so we can manage our memory correctly.

A struct that is a member of a class, will be allocated on the heap:

public struct MyStruct
{
}
Public class MyClass
{
MyStruct m_myStruct;
}

In this case, an object of MyClass will be allocated on the heap, because it is a Class. Therefore all its member variables, even if they are structs, will be allocated on the heap as well.

m_myStruct will be deallocated automatically when the MyClass object is deallocated.

Structs with Arrays

If a struct has an array as a member variable, the array will need to be allocated on the stack:

public struct MyStruct
{
public int Integer;
public int[] Array;
}
MyStruct myStruct;

We can use myStruct.Integer immediately:

myStruct.Integer = 10;

But to use myStruct.Array we first need to allocate it:

myStruct.Array = new int[1024];

That means myStruct.Array will be allocated on the heap and you need to pay special attention to when it will be deallocated.

Structs with Strings

The same thing happens with strings:

public struct MyStruct
{
public String MyString;
}
MyStruct myStruct;

When we call:

myStruct.MyString = "Hello World";

The string is allocated on the heap, that is because we don’t know beforehand how big the string is.

myStruct.MyString actually points to an address in memory where the string “Hello World” is allocated.

If we change the string to a different value, for example:

myStruct.MyString = "Good Morning";

then myStruct.MyString is now pointing to a new memory address, where “Good Morning” is allocated.

The old memory block, where “Hello World” was allocated is now marked as unused and will be removed by the Garbage Collector.

Structs with Objects

If your struct includes an Object, then that object will naturally be allocated on the heap, including GameObjects:

public struct MyStruct
{
public GameObject GO;
}
MyStruct myStruct;

myStruct.GO points to a memory address where GameObject GO is located.

myStruct.GO initially points to null. To use it you’d need to either assign it to an existing GameObject or Instantiate a new one.

Keep Structs Small

Microsoft recommends only using a struct if the instance size of your struct is at most 16 bytes.

Coincidentally, the size of Vector4 in Unity is exactly 16 bytes.

In short, if your struct holds variables that are going to be heap allocated, or is larger than 16 bytes, then you are better off making it a class.

Part 6 — https://medium.com/@nitzanwilnai/dod-unity-memory-best-practices-part-6-98234b2511e0

--

--