During my work, I always faced situations when at the initialization stage of the application there were lots of allocations. Of course, it evolved whenever the project got bigger and bigger.

Thus one question arose whenever a certain threshold was reached: is it possible to reserve memory before it is needed? 1

Why even care?

To understand why this part is important we must return to the basics: how allocator and GC work for managed memory in Unity 2.

I omit certain details about the guts of managed memory, those who want to know more can read in official documentation.

Get back to the allocation and garbage collection process:

  1. When some memory is requested allocator tries to fit an object in the already allocated region(s) of the memory
  2. If there is no region part where the new object could fit, the allocator can 3 start a garbage collection process to clean up memory from objects and find a place for a new allocation
  3. If the previous step fails allocator will reserve a new memory region of a certain size
  4. In the end if there is no memory: NotEnoughMemoryException will appear (in the managed region)

Thus this step happens for every single allocation in our application. This doesn’t seem much of an issue for tens or hundreds of allocations, but what if we talk about tens of thousands? Hundreds of thousands of allocations?

Official documentation

Official Unity documentation provides some information about controlling allocators. It gives us the ability to control block size, granularity, etc. of certain allocators but there is no ability to control preallocated size before it will be used.

Hence straightforward way to use public API is closed, nevertheless, it doesn’t mean we don’t have options.

First option that we, we can use is simple and dangerous in the same time - reserve memory through the manual creation of large blocks of data:

public class HeapAllocator : MonoBehaviour
{
	private const int MBByte = 1024 * 1024;

	private void Start()
	{
		ReserveMemory(100);
	}

	public void ReserveMemory(int megabytes)
	{
		_ = new long[MBByte * megabytes / 8]; //This is just an example, you can control granularity as you wish using different data types or data structures
	}
}

Thus using a blunt technique we can reserve as much memory as we need at the start of the application but still 1 garbage collection process will occur to clean up this memory.

Manual Reservation

But it has certain restrictions that prevent its usage in production:

  1. The first dead end awaits in case there will be a necessity to reserver memory in the middle of runtime (for example after 5 minutes of running) because it isn’t determined how and where newly allocated objects will fit (in the already allocated region or a new one).
  2. Because the garbage collection process isn’t deterministic making allocations right after reservation could lead to the allocation of a new region instead of fitting in the preallocated one because GC may not clean out preallocated objects.

Well, Unity has an interesting backdoor…

Private functionality

At one of the Unites, developers of The Elder Scrolls Blades mentioned that they controlled heap growth in Unity using heap growth divisor4. So, maybe there is a functionality to reserve memory in a managed region?

Because internally Unity uses open source Boehm-Demers-Weiser Garbage Collector (5 which has significant differences from Mono and especially from modern .Net GC) we can go straightly to the public GitHub repository, here is what we will see there among different public APIs:

GC_expand_hp(bytes) - Explicitly increase the heap size.

This is exactly what we need!

But before, almost everyone encountered the next attribute during their work:

[DllImport ("__Internal")]

Every piece of code that is out of the C# area and written in the native language will be exposed through it. But most importantly it can expose native unity code 6.

public static class HeapControl
{
    [DllImport ("__Internal")]
    private static extern int GC_expand_hp (int bytes);
    
    public static void ReserveMemory(int bytes)
    {
            GC_expand_hp(bytes);
    }
}

What we see:

Exposed reservation

Exactly the same picture as in the case with hacked reservation but without any drawbacks of the previous one. 7

In the end, by exposing of Boehm GC API we are able to reserve memory without any drawbacks.

One more thing: what other stuff is available to use in Unity using DLLImport?


  1. Not a kind of an issue you will face when work with languages and engines with manual memory management ↩︎

  2. Unity has custom implementation of garbage collection and allocations for managed region using Boehm GC ↩︎

  3. If you interested you can dive in GC source code ↩︎

  4. https://youtu.be/KbxiGH6igBk?t=2343 - Talk from Unite about Heap Growth ↩︎

  5. https://github.com/ivmai/bdwgc - Boehm GC Source code ↩︎

  6. Not all, but still ↩︎

  7. Tested in Unity 2021.3.30 and Unity 2022.3.10 ↩︎