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.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.
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 job | Yes | No | 64 |
Allocator.TempJob |
Within 4 frames of creation | No | Yes | 16 |
Allocator.Persistent |
Indefinite | No | Yes | 16 |
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.
The allocators map to memory labels and Memory Profiler regions as follows:
Allocator.Temp maps to the labelTempAlloc 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 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:
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.
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];
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:
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.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.
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.
Each collection retains a reference to the allocator that allocated its memory. This is because you must specify the allocator to deallocate its memory.
Unsafe- collection’s Dispose method deallocates its memory.Native- collection’s Dispose method deallocates its memory and frees the handles needed for safety checks.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);
The IsCreated property of a collection is false only in the following cases:
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.