Documentation

Unity Engine


User Manual

Script Reference

Unity Engine


Introduction to allocators

Understand how to use an allocator to manage unmanaged memory.
Read time 9 minutesLast updated 8 days ago

An allocator controls the allocation of unmanaged memory. Different allocators organize and track their memory in different ways. The
Unity.Collections
APIs include the following allocators:
  • Allocator.Temp
    : The fastest allocator, for short-lived allocations. You can't pass this allocator to a job.
  • Allocator.TempJob
    : A short-lived allocator that you can pass into jobs.
  • Allocator.Persistent
    : The slowest allocator for indefinite lifetime allocations. You can pass this allocator to a job.
  • Rewindable allocator: A custom allocator that's fast and thread safe, and can rewind and free all your allocations at one point.
To use these types, pass an
Allocator
enum value to select the native allocator type's memory label to use. Alternatively, for low-level allocations where you need custom categorization, you can create and pass a
MemoryLabel
instance directly to allocation functions. If a chosen
Temp
allocator is full, Unity chooses a fallback allocator instead (usually
TempJob
, or
Temp
overflow).
The Entities package has its own set of custom prebuilt allocators. For more information, refer to Memory allocators.

Allocator feature comparison

The following table compares the features of the different built-in allocators:

Allocator type

Lifetime

Automatically freed allocations

Can pass to jobs

Min Allocation Alignment (bytes)

Allocator.Temp
A frame or a jobYesNo64
Allocator.TempJob
Within 4 frames of creationNoYes16
Allocator.Persistent
IndefiniteNoYes16
Alternatively, you can create a custom rewindable allocator, which can be passed to jobs, has an indefinite allocation lifetime, and a 64 byte minimum allocation alignment. For more information, refer to Rewindable allocator.

Memory label mapping

The allocators map to memory labels and Memory Profiler regions as follows:
  • Allocator.Temp
    maps to the label
    TempAlloc
    and a Thread-local Temporary Allocator. Depending on the allocating thread, this might show in the Memory Profiler under the allocator region
    ALLOC_TEMP_TLS
    as
    ALLOC_TEMP_MAIN
    ,
    ALLOC_TEMP_THREAD
    ,
    ALLOC_TEMP_<ThreadName>
    or under
    Native > Unity Subsystems > UnsafeUtility > Malloc(Temp) PoolUnsafeUtility
    with allocations under the label
    Utility
    .
  • Allocator.TempJob
    maps to
    TempJobAlloc
    and is reported in the Memory Profiler under the allocator region
    ALLOC_TEMP_JOB_4_FRAMES
    potentially with the post-fix
    (JobTemp)
    .
  • Allocator.Persistent
    maps to
    Default
    /
    Malloc
    and might show under the allocator region or
    ALLOC_DEFAULT
    and either
    ALLOC_DEFAULT_MAIN
    or
    ALLOC_DEFAULT_THREAD
    (or depending on the size of the allocation,
    ALLOC_BUCKET
    ) under
    Native >Unity Subsystems >UnsafeUtility >Malloc(Persistent) UnsafeUtility
    .

Temp allocator

Temp
is Unity's temporary allocator type. It's designed for small, short-lived allocations that can't cross a frame boundary, such as temporary allocations made within a single job. The
Temp
type is a stack type allocator for small amounts of memory that Unity allocates from the main memory pool.
Each frame, the main thread creates a
Temp
allocator which it deallocates in its entirety at the end of the frame. Each job also creates one
Temp
allocator per thread, and deallocates them in their entirety at the end of the job. Because a
Temp
allocator gets discarded as a whole, you don't need to manually deallocate
Temp
allocations, and doing so has no effect.
Temp
allocations are only safe to use in the thread and the scope where they were allocated. While you can make
Temp
allocations within a job, you can't pass main thread
Temp
allocations into a job. For example, you can't pass a native array that's
Temp
allocated in the main thread into a job.
There are multiple
Temp
allocators, and each thread that needs an allocator has one. Unity recycles
Temp
allocators each frame ensuring a clean memory usage pattern. However, if memory spills over into the fallback allocators, their memory might get fragmented.
You can use
Profiler.GetTempAllocatorSize
and
Profiler.SetTempAllocatorRequestedSize
to get and set the size for
Temp
allocators. The minimum alignment of
Temp
allocations is 64 bytes.
The sizes for the
Temp
allocator default to:
  • Editor: 16 MB main thread, 256 KB worker threads.
  • Players: 4 MB main thread, 256 KB worker threads.
If these sizes are exceeded, allocations on the main thread fall back to the
TempJob
allocator, and on other threads they fall back to a
Temp
overflow allocator. For more information on temporary native allocations, refer to Thread storage local (TLS) allocator.

Shared AtomicSafetyHandle for Temp allocations

All containers allocated with
Allocator.Temp
on the same thread use a shared
AtomicSafetyHandle
instance rather than each having their own. Most of the time, this isn't an issue because you can't pass
Temp
allocated collections into a job.
However, when you use
NativeHashMap
,
NativeParallelMultiHashMap
,
NativeHashSet
, and
NativeList
together with their secondary safety handle, this shared
AtomicSafetyHandle
instance is a problem.
A secondary safety handle ensures a
NativeArray
that aliases a
NativeList
is invalidated when the
NativeList
is reallocated due to resizing.
Operations that invalidate an enumerator for these collection types, or that invalidate the
NativeArray
that
NativeList.AsArray
returns, also invalidate all other previously acquired enumerators. For example, the following throws an error when safety checks are enabled:
var list = new NativeList<int>(Allocator.Temp);list.Add(1);// This array uses the secondary safety handle of the list, which is// shared between all Allocator.Temp allocations.var array = list.AsArray();var list2 = new NativeHashSet<int>(Allocator.Temp);// This invalidates the secondary safety handle, which is also used// by the list above.list2.TryAdd(1);// This throws an InvalidOperationException because the shared safety// handle was invalidated.var x = array[0];

TempJob allocator

This type uses a linear style allocator which Unity stores in a
TempJob
area that it allocates from main memory. It's designed for larger temporary allocations that are passed from job to job, and those that might carry data between frames. If the allocations aren't disposed of within 4 frames of their creation, a warning is displayed in the Unity Editor.
Note
If the memory available is exhausted, Unity makes the overspill allocations in main memory (malloc). Most platforms can have up to 64 MB of
TempJob
memory, but on smaller memory systems this limit can be as low as 16 MB.
If this allocator is full, allocations fall back to a
TempJob
overflow allocator. For more information on temporary job allocations, refer to Thread safe linear allocator.
You must deallocate
TempJob
allocations within 4 frames of their creation. The 4-frame limit provides some margin for allocations that need to persist across a few frames. The minimum alignment of
TempJob
allocations is 16 bytes.
If a
TempJob
allocation isn't disposed within 4 frames, Unity writes a warning message but the allocation remains valid. Unity doesn't automatically deallocate such allocation, and you must still explicitly dispose it in your code to free the memory.
Exceeding the 4-frame limit has the following consequences:
  • Performance degradation: The
    TempJob
    allocator has a fixed memory pool. If long-lived allocations accumulate and exhaust this pool, subsequent allocations fall back to
    Allocator.Persistent
    , which is significantly slower.
  • Memory leak reports: If allocations still exist when the
    TempJob
    allocator is disposed (for example, when the application exits), Unity reports them as memory leaks when native leak detection is enabled.
For
Native
collection types, safety-handle checks (via
AtomicSafetyHandle
) validate correct usage and disposal.
Unsafe
collection types don't have these safety handles, so they don't perform those usage/disposal validation checks.
A separate warning mechanism for when a
TempJob
allocation exceeds 4 frames is driven by allocation tracking rather than safety handles. It applies whenever leak detection is enabled. Because
Unsafe
collections still allocate through tracked paths (for example,
UnsafeList
uses
UnsafeUtility.MallocTracked
), overdue
TempJob
allocations from
Unsafe
collections can still trigger this warning independently of the safety-handle checks. For more information, refer to
NativeLeakDetection
.
If your job or allocation might regularly exceed 4 frames, use
Allocator.Persistent
instead.
If you need an intermediate-lifetime allocator optimized for durations between
TempJob
and
Persistent
, consider using a custom allocator or a rewindable allocator.

Persistent allocator

This allocator allocates memory with no time restrictions, is only limited by the amount of free memory on a device, and therefore has no fallback. It can exist for as long as you need it, but the leak detection system only warns you if you don't free the allocated memory and if your handle to the memory was garbage collected. As such, you must deallocate a
Persistent
allocation when you no longer need it. Allocating persistent memory is slower than allocating temporary memory. The minimum alignment of
Persistent
allocations is 16 bytes.

Deallocating memory

Each collection retains a reference to the allocator that allocated its memory. This is because you must specify the allocator to deallocate its memory.
  • An
    Unsafe-
    collection's
    Dispose
    method deallocates its memory.
  • A
    Native-
    collection's
    Dispose
    method deallocates its memory and frees the handles needed for safety checks.
  • An enumerator's
    Dispose
    method does nothing. The method exists only to fulfill the
    IEnumerator<T>
    interface.
To dispose a collection after the jobs that need it have run, you can use the
Dispose(JobHandle)
method. This creates and schedules a job that disposes of the collection, and this new job takes the input handle as its dependency. Effectively, the method defers disposal until after the dependency runs:
NativeArray<int> nums = new NativeArray<int>(10, Allocator.TempJob); // Create and schedule a job that uses the array. ExampleJob job = new ExampleJob { Nums = nums }; JobHandle handle = job.Schedule(); // Create and schedule a job that will dispose the array after the ExampleJob has run. // Returns the handle of the new job. handle = nums.Dispose(handle);

IsCreated property

The
IsCreated
property of a collection is false only in the following cases:
  • Immediately after creating a collection with its default constructor.
  • After
    Dispose
    has been called on the collection.
Important
Don't use a collection's default constructor. The constructor is only available because C# requires that all structs have a public default constructor.
Calling
Dispose
on a collection sets
IsCreated
to false only for that struct, and not in any copies of the struct.
IsCreated
might still be true even after the collection's underlying memory is deallocated in the following situations:
  • Dispose
    was called on a different copy of the struct.
  • The underlying memory was deallocated via an alias.

Additional resources