# Create and use a custom allocator

> Create and use a custom allocator for unmanaged collections.

Once you've [defined a custom allocator](/engine/7000.0/manual/analysis/performance-memory/collections/allocation/allocator-custom-define.md), you can add it to your structure or class.

## Declare and create a custom allocator

The first step is to declare and create the custom allocator. You must do the following:

* Allocate memory to hold the custom allocator.
* Register the allocator by adding an entry in a global allocator table.
* Initialize the allocator if necessary.

The wrapper [`AllocatorHelper`](/engine/7000.0/script-reference/unity/collections/allocatorhelper1.md) helps in creating a custom allocator. The following examples show how to declare and create a custom allocator defined in the [Example custom allocator](/engine/7000.0/manual/analysis/performance-memory/collections/allocation/allocator-custom-define.md).

```c#
// Example user structure that contains the custom allocator
internal struct ExampleCustomAllocatorStruct
{
    // Use AllocatorHelper to help creating the example custom alloctor
    AllocatorHelper<ExampleCustomAllocator> customAllocatorHelper;

    // Custom allocator property for accessibility
    public ref ExampleCustomAllocator customAllocator => ref customAllocatorHelper.Allocator;

    // Create the example custom allocator
    void CreateCustomAllocator(AllocatorManager.AllocatorHandle backgroundAllocator, byte initialValue)
    {
        // Allocate the custom allocator from backgroundAllocator and register the allocator
        customAllocatorHelper = new AllocatorHelper<ExampleCustomAllocator>(backgroundAllocator);

        // Set the initial value to initialize the memory
        customAllocator.Initialize(initialValue);
    }
}
```

## Use a custom allocator to allocate memory

For `Native-` collection types, allocation from a custom allocator is similar to built-in allocators. The notable exception is when creating or deallocating a `NativeArray`, for which you must use [`CollectionHelper.CreateNativeArray`](/engine/7000.0/script-reference/unity/collections/collectionhelper/createnativearray.md) and [`CollectionHelper.Dispose`](/engine/7000.0/script-reference/unity/collections/collectionhelper/dispose.md) respectively.

When you use a custom allocator to create a `Native-` collection type, its safety handle is added to the list of child safety handles of the custom allocator. When you rewind the allocator handle of a custom allocator, it invalidates and unregisters all its child allocators, and invalidates all its child safety handles. For `Native-` collection types, the disposal safety checks throw an exception if the allocator handle has rewound.

For `Unsafe-` collection types such as [`UnsafeList<T>`](/engine/7000.0/script-reference/unity/collections/lowlevel/unsafe/unsafelist1.md), [`UnsafeHashMap<TKey, TValue>`](/engine/7000.0/script-reference/unity/collections/lowlevel/unsafe/unsafehashmap2.md), and [`UnsafeStream`](/engine/7000.0/script-reference/unity/collections/lowlevel/unsafe/unsafestream.md), pass the custom allocator's [`AllocatorManager.Allocate`](/engine/7000.0/script-reference/unity/collections/allocatormanager/allocate.md) to the collection's constructor. The collection retains this handle and uses it when you call the collection's own `Dispose` method, so you don't need to allocate or free its memory manually.

To allocate raw memory from a custom allocator, use [`AllocatorManager.Allocate`](/engine/7000.0/script-reference/unity/collections/allocatormanager/allocate.md) to allocate the memory and [`AllocatorManager.Free`](/engine/7000.0/script-reference/unity/collections/allocatormanager/free.md) to deallocate it. The following example demonstrates this raw byte-buffer case:

```cs
// Sample code to use the custom allocator to allocate containers
    public void UseCustomAllocator(out NativeArray<int> nativeArray, out NativeList<int> nativeList)
    {
        // Use custom allocator to allocate a native array and check initial value.
        nativeArray = CollectionHelper.CreateNativeArray<int, ExampleCustomAllocator>(100, ref customAllocator, NativeArrayOptions.UninitializedMemory);
        Assert.AreEqual(customAllocator.InitialValue, (byte)nativeArray[0] & 0xFF);
        nativeArray[0] = 0xFE;

        // Use custom allocator to allocate a native list and check initial value.
        nativeList = new NativeList<int>(customAllocator.Handle);
        for (int i = 0; i < 50; i++)
        {
            nativeList.Add(i);
        }

        unsafe
        {
            // Use custom allocator to allocate a byte buffer.
            byte* bytePtr = (byte*)AllocatorManager.Allocate(ref customAllocator, sizeof(byte), sizeof(byte), 10);
            Assert.AreEqual(customAllocator.InitialValue, bytePtr[0]);

            // Free the byte buffer.
            AllocatorManager.Free(customAllocator.ToAllocator, bytePtr, 10);
        }
    }
    #endregion // allocator-custom-use

    public int AllocationCount => customAllocator.AllocationCount;

    public void UseCustomAllocatorHandle(out NativeArray<int> nativeArray, out NativeList<int> nativeList)
    {
        // Use custom allocator to allocate a native array and check initial value.
        nativeArray = CollectionHelper.CreateNativeArray<int>(100, customAllocator.ToAllocator, NativeArrayOptions.UninitializedMemory);
        Assert.AreEqual(customAllocator.InitialValue, (byte)nativeArray[0] & 0xFF);
        nativeArray[0] = 0xFE;

        // Use custom allocator to allocate a native list and check initial value.
        nativeList = new NativeList<int>(customAllocator.Handle);
        for (int i = 0; i < 50; i++)
        {
            nativeList.Add(i);
        }

        unsafe
        {
            // Use custom allocator to allocate a byte buffer.
            byte* bytePtr = (byte*)AllocatorManager.Allocate(ref customAllocator, sizeof(byte), sizeof(byte), 10);
            Assert.AreEqual(customAllocator.InitialValue, bytePtr[0]);

            AllocatorManager.Free(customAllocator.ToAllocator, bytePtr, 10);
        }
    }
}

internal class ExampleCustomAllocatorStructUsage
{
    const int IntialValue = 0xAB;

    // Test code.
    [Test]
    public void UseCustomAllocator_Works()
    {
        ExampleCustomAllocatorStruct exampleStruct = new ExampleCustomAllocatorStruct(IntialValue);

        // Allocate native array and native list from the custom allocator
        exampleStruct.UseCustomAllocator(out NativeArray<int> nativeArray, out NativeList<int> nativeList);

        // Able to access the native array and native list
        Assert.AreEqual(nativeArray[0], 0xFE);
        Assert.AreEqual(nativeList[10], 10);

        // Need to use CollectionHelper.DisposeNativeArray to dispose the native array from a custom allocator
        CollectionHelper.Dispose(nativeArray) ;
        // Dispose the native list
        nativeList.Dispose();

#if ENABLE_UNITY_COLLECTIONS_CHECKS
        // Object disposed exception throws because nativeArray is already disposed
        Assert.Throws<ObjectDisposedException>(() =>
        {
            nativeArray[0] = 0xEF;
        });

        // Object disposed exception throws because nativeList is already disposed
        Assert.Throws<ObjectDisposedException>(() =>
        {
            nativeList[10] = 0x10;
        });
#endif

        // Check allocation count after dispose the native array and native list
        Assert.AreEqual(0, exampleStruct.AllocationCount);

        exampleStruct.Dispose();
    }

    [Test]
    public void UseCustomAllocatorHandle_Works()
    {
        ExampleCustomAllocatorStruct exampleStruct = new ExampleCustomAllocatorStruct(IntialValue);

        // Allocate native array and native list from the custom allocator handle
        exampleStruct.UseCustomAllocatorHandle(out NativeArray<int> nativeArray, out NativeList<int> nativeList);

        // Able to access the native array and native list
        Assert.AreEqual(nativeArray[0], 0xFE);
        Assert.AreEqual(nativeList[10], 10);

        // Need to use CollectionHelper.DisposeNativeArray to dispose the native array from a custom allocator
        CollectionHelper.Dispose(nativeArray);
        nativeList.Dispose();

#if ENABLE_UNITY_COLLECTIONS_CHECKS
        // Object disposed exception throws because nativeArray is already disposed
        Assert.Throws<ObjectDisposedException>(() =>
        {
            nativeArray[0] = 0xEF;
        });

        // Object disposed exception throws because nativeList is already disposed
        Assert.Throws<ObjectDisposedException>(() =>
        {
            nativeList[10] = 0x10;
        });
#endif

        Assert.AreEqual(0, exampleStruct.AllocationCount);

        exampleStruct.Dispose();
    }

    [Test]
    public void CustomAllocatorHandle_MultiThreadWorks()
    {
        ExampleCustomAllocatorStruct exampleStruct = new ExampleCustomAllocatorStruct(IntialValue);

        var taskList = new List<Task>();

        // create 128 native array with another threads
        for (var i = 0; i < 128; i++)
        {
            var task = Task.Run(() =>
            {
                var nativeArray = CollectionHelper.CreateNativeArray<int, ExampleCustomAllocator>(100, ref exampleStruct.customAllocator,
                    NativeArrayOptions.UninitializedMemory);

                CollectionHelper.Dispose(nativeArray);
            });

            taskList.Add(task);
        }

        Task.WaitAll(taskList.ToArray());

        exampleStruct.Dispose();
    }
}
#endregion // allocator-custom-user-struct
```

## Dispose a custom allocator

To dispose a custom allocator, the following must happen:

* The custom allocator must rewind its allocator handle which invalidates and unregisters all the allocator handle's child allocators, and invalidates all its child safety handles.
* You must unregister the allocator.
* You must dispose the memory used to store the allocator.

The example method `DisposeCustomAllocator` in the user structure shows how to dispose a custom allocator:

```cs
// Dispose the custom allocator
    void DisposeCustomAllocator()
    {
        // Dispose the custom allocator
        customAllocator.Dispose();

        // Unregister the custom allocator and dispose it
        customAllocatorHelper.Dispose();
    }
    #endregion // allocator-custom-dispose

    public ExampleCustomAllocatorStruct(byte initialValue)
    {
        this = default;
        CreateCustomAllocator(Allocator.Persistent, initialValue);
    }

    public void Dispose()
    {
        DisposeCustomAllocator();
    }

    #region allocator-custom-use
    // Sample code to use the custom allocator to allocate containers
    public void UseCustomAllocator(out NativeArray<int> nativeArray, out NativeList<int> nativeList)
    {
        // Use custom allocator to allocate a native array and check initial value.
        nativeArray = CollectionHelper.CreateNativeArray<int, ExampleCustomAllocator>(100, ref customAllocator, NativeArrayOptions.UninitializedMemory);
        Assert.AreEqual(customAllocator.InitialValue, (byte)nativeArray[0] & 0xFF);
        nativeArray[0] = 0xFE;

        // Use custom allocator to allocate a native list and check initial value.
        nativeList = new NativeList<int>(customAllocator.Handle);
        for (int i = 0; i < 50; i++)
        {
            nativeList.Add(i);
        }

        unsafe
        {
            // Use custom allocator to allocate a byte buffer.
            byte* bytePtr = (byte*)AllocatorManager.Allocate(ref customAllocator, sizeof(byte), sizeof(byte), 10);
            Assert.AreEqual(customAllocator.InitialValue, bytePtr[0]);

            // Free the byte buffer.
            AllocatorManager.Free(customAllocator.ToAllocator, bytePtr, 10);
        }
    }
    #endregion // allocator-custom-use

    public int AllocationCount => customAllocator.AllocationCount;

    public void UseCustomAllocatorHandle(out NativeArray<int> nativeArray, out NativeList<int> nativeList)
    {
        // Use custom allocator to allocate a native array and check initial value.
        nativeArray = CollectionHelper.CreateNativeArray<int>(100, customAllocator.ToAllocator, NativeArrayOptions.UninitializedMemory);
        Assert.AreEqual(customAllocator.InitialValue, (byte)nativeArray[0] & 0xFF);
        nativeArray[0] = 0xFE;

        // Use custom allocator to allocate a native list and check initial value.
        nativeList = new NativeList<int>(customAllocator.Handle);
        for (int i = 0; i < 50; i++)
        {
            nativeList.Add(i);
        }

        unsafe
        {
            // Use custom allocator to allocate a byte buffer.
            byte* bytePtr = (byte*)AllocatorManager.Allocate(ref customAllocator, sizeof(byte), sizeof(byte), 10);
            Assert.AreEqual(customAllocator.InitialValue, bytePtr[0]);

            AllocatorManager.Free(customAllocator.ToAllocator, bytePtr, 10);
        }
    }
}

internal class ExampleCustomAllocatorStructUsage
{
    const int IntialValue = 0xAB;

    // Test code.
    [Test]
    public void UseCustomAllocator_Works()
    {
        ExampleCustomAllocatorStruct exampleStruct = new ExampleCustomAllocatorStruct(IntialValue);

        // Allocate native array and native list from the custom allocator
        exampleStruct.UseCustomAllocator(out NativeArray<int> nativeArray, out NativeList<int> nativeList);

        // Able to access the native array and native list
        Assert.AreEqual(nativeArray[0], 0xFE);
        Assert.AreEqual(nativeList[10], 10);

        // Need to use CollectionHelper.DisposeNativeArray to dispose the native array from a custom allocator
        CollectionHelper.Dispose(nativeArray) ;
        // Dispose the native list
        nativeList.Dispose();

#if ENABLE_UNITY_COLLECTIONS_CHECKS
        // Object disposed exception throws because nativeArray is already disposed
        Assert.Throws<ObjectDisposedException>(() =>
        {
            nativeArray[0] = 0xEF;
        });

        // Object disposed exception throws because nativeList is already disposed
        Assert.Throws<ObjectDisposedException>(() =>
        {
            nativeList[10] = 0x10;
        });
#endif

        // Check allocation count after dispose the native array and native list
        Assert.AreEqual(0, exampleStruct.AllocationCount);

        exampleStruct.Dispose();
    }

    [Test]
    public void UseCustomAllocatorHandle_Works()
    {
        ExampleCustomAllocatorStruct exampleStruct = new ExampleCustomAllocatorStruct(IntialValue);

        // Allocate native array and native list from the custom allocator handle
        exampleStruct.UseCustomAllocatorHandle(out NativeArray<int> nativeArray, out NativeList<int> nativeList);

        // Able to access the native array and native list
        Assert.AreEqual(nativeArray[0], 0xFE);
        Assert.AreEqual(nativeList[10], 10);

        // Need to use CollectionHelper.DisposeNativeArray to dispose the native array from a custom allocator
        CollectionHelper.Dispose(nativeArray);
        nativeList.Dispose();

#if ENABLE_UNITY_COLLECTIONS_CHECKS
        // Object disposed exception throws because nativeArray is already disposed
        Assert.Throws<ObjectDisposedException>(() =>
        {
            nativeArray[0] = 0xEF;
        });

        // Object disposed exception throws because nativeList is already disposed
        Assert.Throws<ObjectDisposedException>(() =>
        {
            nativeList[10] = 0x10;
        });
#endif

        Assert.AreEqual(0, exampleStruct.AllocationCount);

        exampleStruct.Dispose();
    }

    [Test]
    public void CustomAllocatorHandle_MultiThreadWorks()
    {
        ExampleCustomAllocatorStruct exampleStruct = new ExampleCustomAllocatorStruct(IntialValue);

        var taskList = new List<Task>();

        // create 128 native array with another threads
        for (var i = 0; i < 128; i++)
        {
            var task = Task.Run(() =>
            {
                var nativeArray = CollectionHelper.CreateNativeArray<int, ExampleCustomAllocator>(100, ref exampleStruct.customAllocator,
                    NativeArrayOptions.UninitializedMemory);

                CollectionHelper.Dispose(nativeArray);
            });

            taskList.Add(task);
        }

        Task.WaitAll(taskList.ToArray());

        exampleStruct.Dispose();
    }
}
#endregion // allocator-custom-user-struct
```

## Full example of a custom allocator

The following is a full example of how to use a custom allocator:

```cs
// Example user structure that contains the custom allocator
internal struct ExampleCustomAllocatorStruct
{
    AllocatorHelper<ExampleCustomAllocator> customAllocatorHelper;

    public ref ExampleCustomAllocator customAllocator => ref customAllocatorHelper.Allocator;

    // Create the example custom allocator
    void CreateCustomAllocator(AllocatorManager.AllocatorHandle backgroundAllocator, byte initialValue)
    {
        // Allocate the custom allocator from backgroundAllocator and register the allocator
        customAllocatorHelper = new AllocatorHelper<ExampleCustomAllocator>(backgroundAllocator);

        // Set the initial value to initialize the memory
        customAllocator.Initialize(initialValue);
    }

    #region allocator-custom-dispose
    // Dispose the custom allocator
    void DisposeCustomAllocator()
    {
        // Dispose the custom allocator
        customAllocator.Dispose();

        // Unregister the custom allocator and dispose it
        customAllocatorHelper.Dispose();
    }
    #endregion // allocator-custom-dispose

    public ExampleCustomAllocatorStruct(byte initialValue)
    {
        this = default;
        CreateCustomAllocator(Allocator.Persistent, initialValue);
    }

    public void Dispose()
    {
        DisposeCustomAllocator();
    }

    #region allocator-custom-use
    // Sample code to use the custom allocator to allocate containers
    public void UseCustomAllocator(out NativeArray<int> nativeArray, out NativeList<int> nativeList)
    {
        // Use custom allocator to allocate a native array and check initial value.
        nativeArray = CollectionHelper.CreateNativeArray<int, ExampleCustomAllocator>(100, ref customAllocator, NativeArrayOptions.UninitializedMemory);
        Assert.AreEqual(customAllocator.InitialValue, (byte)nativeArray[0] & 0xFF);
        nativeArray[0] = 0xFE;

        // Use custom allocator to allocate a native list and check initial value.
        nativeList = new NativeList<int>(customAllocator.Handle);
        for (int i = 0; i < 50; i++)
        {
            nativeList.Add(i);
        }

        unsafe
        {
            // Use custom allocator to allocate a byte buffer.
            byte* bytePtr = (byte*)AllocatorManager.Allocate(ref customAllocator, sizeof(byte), sizeof(byte), 10);
            Assert.AreEqual(customAllocator.InitialValue, bytePtr[0]);

            // Free the byte buffer.
            AllocatorManager.Free(customAllocator.ToAllocator, bytePtr, 10);
        }
    }
    #endregion // allocator-custom-use

    public int AllocationCount => customAllocator.AllocationCount;

    public void UseCustomAllocatorHandle(out NativeArray<int> nativeArray, out NativeList<int> nativeList)
    {
        // Use custom allocator to allocate a native array and check initial value.
        nativeArray = CollectionHelper.CreateNativeArray<int>(100, customAllocator.ToAllocator, NativeArrayOptions.UninitializedMemory);
        Assert.AreEqual(customAllocator.InitialValue, (byte)nativeArray[0] & 0xFF);
        nativeArray[0] = 0xFE;

        // Use custom allocator to allocate a native list and check initial value.
        nativeList = new NativeList<int>(customAllocator.Handle);
        for (int i = 0; i < 50; i++)
        {
            nativeList.Add(i);
        }

        unsafe
        {
            // Use custom allocator to allocate a byte buffer.
            byte* bytePtr = (byte*)AllocatorManager.Allocate(ref customAllocator, sizeof(byte), sizeof(byte), 10);
            Assert.AreEqual(customAllocator.InitialValue, bytePtr[0]);

            AllocatorManager.Free(customAllocator.ToAllocator, bytePtr, 10);
        }
    }
}

internal class ExampleCustomAllocatorStructUsage
{
    const int IntialValue = 0xAB;

    // Test code.
    [Test]
    public void UseCustomAllocator_Works()
    {
        ExampleCustomAllocatorStruct exampleStruct = new ExampleCustomAllocatorStruct(IntialValue);

        // Allocate native array and native list from the custom allocator
        exampleStruct.UseCustomAllocator(out NativeArray<int> nativeArray, out NativeList<int> nativeList);

        // Able to access the native array and native list
        Assert.AreEqual(nativeArray[0], 0xFE);
        Assert.AreEqual(nativeList[10], 10);

        // Need to use CollectionHelper.DisposeNativeArray to dispose the native array from a custom allocator
        CollectionHelper.Dispose(nativeArray) ;
        // Dispose the native list
        nativeList.Dispose();

#if ENABLE_UNITY_COLLECTIONS_CHECKS
        // Object disposed exception throws because nativeArray is already disposed
        Assert.Throws<ObjectDisposedException>(() =>
        {
            nativeArray[0] = 0xEF;
        });

        // Object disposed exception throws because nativeList is already disposed
        Assert.Throws<ObjectDisposedException>(() =>
        {
            nativeList[10] = 0x10;
        });
#endif

        // Check allocation count after dispose the native array and native list
        Assert.AreEqual(0, exampleStruct.AllocationCount);

        exampleStruct.Dispose();
    }

    [Test]
    public void UseCustomAllocatorHandle_Works()
    {
        ExampleCustomAllocatorStruct exampleStruct = new ExampleCustomAllocatorStruct(IntialValue);

        // Allocate native array and native list from the custom allocator handle
        exampleStruct.UseCustomAllocatorHandle(out NativeArray<int> nativeArray, out NativeList<int> nativeList);

        // Able to access the native array and native list
        Assert.AreEqual(nativeArray[0], 0xFE);
        Assert.AreEqual(nativeList[10], 10);

        // Need to use CollectionHelper.DisposeNativeArray to dispose the native array from a custom allocator
        CollectionHelper.Dispose(nativeArray);
        nativeList.Dispose();

#if ENABLE_UNITY_COLLECTIONS_CHECKS
        // Object disposed exception throws because nativeArray is already disposed
        Assert.Throws<ObjectDisposedException>(() =>
        {
            nativeArray[0] = 0xEF;
        });

        // Object disposed exception throws because nativeList is already disposed
        Assert.Throws<ObjectDisposedException>(() =>
        {
            nativeList[10] = 0x10;
        });
#endif

        Assert.AreEqual(0, exampleStruct.AllocationCount);

        exampleStruct.Dispose();
    }

    [Test]
    public void CustomAllocatorHandle_MultiThreadWorks()
    {
        ExampleCustomAllocatorStruct exampleStruct = new ExampleCustomAllocatorStruct(IntialValue);

        var taskList = new List<Task>();

        // create 128 native array with another threads
        for (var i = 0; i < 128; i++)
        {
            var task = Task.Run(() =>
            {
                var nativeArray = CollectionHelper.CreateNativeArray<int, ExampleCustomAllocator>(100, ref exampleStruct.customAllocator,
                    NativeArrayOptions.UninitializedMemory);

                CollectionHelper.Dispose(nativeArray);
            });

            taskList.Add(task);
        }

        Task.WaitAll(taskList.ToArray());

        exampleStruct.Dispose();
    }
}
#endregion // allocator-custom-user-struct
```

## Additional resources

* [Introduction to allocators](/engine/7000.0/manual/analysis/performance-memory/collections/allocation/allocator-overview.md)
* [Aliasing allocators](/engine/7000.0/manual/analysis/performance-memory/collections/allocation/allocator-aliasing.md)
* [Rewindable allocator](/engine/7000.0/manual/analysis/performance-memory/collections/allocation/allocator-rewindable.md)
* [Custom allocator](/engine/7000.0/manual/analysis/performance-memory/collections/allocation/allocator-custom-define.md)
