# Diagnose and fix assembly load context leaks

> Fix AssemblyLoadContext leaks by following the recommended practices for code reload and assembly load context management.

To determine the cause of an assembly load context leak, in the [leak report](/engine/7000.0/manual/programming-environment/code-reload-serialization/code-reload/code-reload-leaks/alc-leak-report.md), read `PathToRoot` from the first item toward the root:

* The first item whose `AssemblyName` is one of your own assemblies identifies which object leaked. This is your starting point, not necessarily the reference to remove.

* The reference that keeps the chain alive is held at the root end of the chain. Inspect `Root.Kind`, together with the last path items before the root, to decide what to remove:

  * `StaticField`: `Root.FieldName` names the field that retains the chain. Clear or reset that field.
  * `Handle`: A `GCHandle` retains the object. The path often doesn't contain the code that allocated the handle (as in the previous example, where `MyGame.AssetIndex` is the only path item). In this case the type and root kind are not enough to find the reference, especially when several handles point to the same type. Restart the Editor with `-record-managed-allocation-callstacks 1` and read `Root.StackTrace`, which gives the call stack where the handle was allocated. Free that specific handle.
  * `Stack`, `Finalizer`, or `Other`: The object is retained by a local on a thread stack, a pending finalizer, or another runtime root. Use the path items to identify the referencing type.

If the type name and root kind still don't locate the code, [record allocation call stacks](#record-allocation-call-stacks) to add a `StackTrace` to each path item and to the handle root.

## Record allocation call stacks ##record-allocation-call-stacks

When the type name and the root kind aren't enough to locate the code that creates the reference, restart the Editor with the `-record-managed-allocation-callstacks` [command-line argument](/engine/7000.0/manual/unity-editor/command-line-arguments/editor.md). The argument adds a `StackTrace` member to the report, and takes the following values:

* `1`: Records call stacks for `GCHandle` allocations.
* `2`: Records call stacks for `GCHandle` allocations and for all managed object allocations.

> **Important:**
>
> Level `2` records a call stack for every managed allocation, which slows the Editor down and increases the memory it uses. Use it to diagnose a specific leak, then restart the Editor without the argument.

## Handles that are never freed

A strong or pinning `GCHandle` keeps its target alive regardless of any other reference, so the context that owns the target's type can't unload. A handle that you allocate with `GCHandleType.Weak` doesn't prevent unloading.

In the report, `Root.Kind` is `Handle`, and `Root.Flags` contains `Pinning` for a pinned handle.

### Resolution

To free the handle before the Editor unloads your code, follow these steps:

1. Declare the type that allocates the handle as `partial`.
2. Add a static method to that type and mark it with [`[OnCodeUnloading]`](/engine/7000.0/script-reference/unity/scripting/lifecyclemanagement/oncodeunloadingattribute.md), which the Editor calls before it unloads your assemblies.
3. Free the handle in that method.

The following example frees a handle that holds an asset index:

```lang-cs
using System.Runtime.InteropServices;
using Unity.Scripting.LifecycleManagement;

public static partial class AssetIndexBridge
{
    private static GCHandle s_IndexHandle;

    public static void Register(AssetIndex index)
    {
        if (s_IndexHandle.IsAllocated)
        {
            s_IndexHandle.Free();
        }

        s_IndexHandle = GCHandle.Alloc(index);
    }


    // Free the handle before the Editor unloads this assembly.
    [OnCodeUnloading]
    static void FreeIndexHandle()
    {
        if (s_IndexHandle.IsAllocated)
        {
            s_IndexHandle.Free();
        }
    }
}
```

## Background tasks that outlive the reload

A thread that runs a method from your assemblies keeps that code alive, because the method sits on the call stack of the thread. A lambda that you pass to `Task.Run` or to a `Thread` constructor also captures the enclosing instance when the lambda reads an instance member, so one long-running task can hold the whole object graph.

In the report, `Root.Kind` is `Stack`, and `PathToRoot` often includes compiler-generated types such as `AsyncStateMachineBox`.

### Resolution

To stop the background work before the Editor unloads your code, follow these steps:

1. Pass a `CancellationToken` to the work, and poll the token in the body of the work.
2. Declare the type that starts the work as `partial`.
3. Add a static method to that type and mark it with `[OnCodeUnloading]`.
4. Cancel the work and wait for it to finish in that method.

The following example stops an indexing task:

```lang-cs
using System.Threading;
using System.Threading.Tasks;
using Unity.Scripting.LifecycleManagement;

public static partial class AssetIndexer
{
    private static CancellationTokenSource s_Cancellation;
    private static Task s_IndexTask;

    public static void StartIndexing()
    {
        // Stop any previous run before replacing the fields.
        StopIndexing();

        s_Cancellation = new CancellationTokenSource();

        // Copy the token into a local variable, so that the lambda captures
        // the token instead of the enclosing type.
        CancellationToken token = s_Cancellation.Token;
        s_IndexTask = Task.Run(() => BuildIndex(token));
    }


    // Stop the task and wait for it to return, so that no thread runs this
    // code when the Editor unloads the assembly.
    [OnCodeUnloading]
    static void StopIndexing()
    {
        if (s_IndexTask == null)
        {
            return;
        }

        s_Cancellation.Cancel();
        s_IndexTask.Wait();
        s_Cancellation.Dispose();

        s_IndexTask = null;
        s_Cancellation = null;
    }

    static void BuildIndex(CancellationToken token)
    {
        while (!token.IsCancellationRequested)
        {
            // Index one asset per iteration.
        }
    }
}
```

## References from code that isn't reloaded

The Editor reloads your scripts and packages, but the .NET base class library and Unity's own assemblies [stay loaded across reloads](/engine/7000.0/manual/programming-environment/code-reload-serialization/code-reload/code-reload-leaks/alc-leak-intro.md#code-reload-and-assembly-load-contexts). A reference from code that isn't reloaded to a type in your code prevents your context from unloading. This reference takes two common forms:

* An event on a long-lived object that your code subscribes to and never unsubscribes from. The delegate holds a reference to your method, and to the enclosing instance for an instance method.
* A static collection or field on a long-lived type that your code adds an entry to and never removes.

In the report, `PathToRoot` ends in types that don't belong to your assemblies, such as `System.EventHandler` followed by `System.AppDomain`. For a static field, `Root.Kind` is `StaticField` and `Root.FieldName` names the field.

### Resolution

To remove the reference before the Editor unloads your code, follow these steps:

1. Declare the type that creates the reference as `partial`.
2. Add a static method to that type and mark it with `[OnCodeUnloading]`.
3. Unsubscribe from the event, or remove the entry from the collection, in that method.

Where your code subscribes from a method marked with [`[OnCodeLoaded]`](/engine/7000.0/script-reference/unity/scripting/lifecyclemanagement/oncodeloadedattribute.md), unsubscribe from the matching `[OnCodeUnloading]` method to keep the two callbacks symmetrical.

The following example unsubscribes from an event on `AppDomain`, which belongs to the base class library and stays loaded across reloads:

```lang-cs
using System;
using Unity.Scripting.LifecycleManagement;
using UnityEngine;

public static partial class CrashReporter
{
    [OnCodeLoaded]
    static void Subscribe()
    {
        AppDomain.CurrentDomain.UnhandledException += LogException;
    }

    // Unsubscribe so that the event no longer references code in this assembly.
    [OnCodeUnloading]
    static void Unsubscribe()
    {
        AppDomain.CurrentDomain.UnhandledException -= LogException;
    }

    static void LogException(object sender, UnhandledExceptionEventArgs args)
    {
        Debug.LogError(args.ExceptionObject);
    }
}
```

## Additional resources

* [Introduction to assembly load context leaks](/engine/7000.0/manual/programming-environment/code-reload-serialization/code-reload/code-reload-leaks/alc-leak-intro.md)
* [Assembly load context leak report reference](/engine/7000.0/manual/programming-environment/code-reload-serialization/code-reload/code-reload-leaks/alc-leak-report.md)
* [Configuring how Unity enters Play mode](/engine/7000.0/manual/programming-environment/code-reload-serialization/code-reload/code-reloading-editor/configurable-enter-play-mode.md)
* [Scripting back ends](/engine/7000.0/manual/programming-environment/scripting-backends.md)
