Documentation

Unity Engine


Unity Engine


Introduction to assembly load context leaks

Introduction to code reload and AssemblyLoadContext leaks.
Read time 4 minutesLast updated 11 days ago

As you develop and iterate in the Unity Editor, Unity automatically loads and reloads your project code as necessary to incorporate changes you make. This process is referred to as code reload. Code reload involves the following key phases:
  1. Recompiling script assemblies.
  2. Serializing managed objects to save their state.
  3. Unloading or destroying existing script assemblies.
  4. Reloading script assemblies.
  5. Deserializing managed objects to restore their state, and performing any necessary initialization.
By default Unity automatically reloads code as part of an Asset Database refresh, when it detects changes to your scripts or other assets.

Code reload and assembly load contexts

The Editor loads assemblies into several instances of
AssemblyLoadContext
(Microsoft). An
AssemblyLoadContext
is a .NET mechanism that groups a set of assemblies so that they load and unload together. The Editor puts code with different lifetimes into separate contexts:
  • The .NET base class library and core scripting assemblies load into a context that stays loaded for the whole Editor session.
  • Unity's Editor and engine assemblies load into long-lived contexts that usually stay loaded across code reloads.
  • Your scripts and package code load into a context that the Editor reloads whenever that code changes.
Because each context loads and unloads as a unit, a code reload unloads only the contexts that need to change, which is usually just the one that holds your code. The long-lived contexts keep their code and data in memory, so the Editor doesn't need to load them again, and reloads finish faster.

Code reload leaks

Unloading a context is cooperative rather than forced. The Editor asks the runtime to unload the context, and the garbage collector releases it only when nothing outside the context still uses it. A context can unload only when both of the following conditions are true:
  • No thread is running a method from an assembly in the context.
  • No code outside the context references an assembly, a type, or an object from the context. Common examples of such references are a static field or a strong
    GCHandle
    .
If either condition isn't met, the old context stays in memory next to the new one that replaces it. This is a code reload leak.
Code in the long-lived contexts stays loaded across reloads, so any reference that it holds to your code also survives the reload. Most leaks come from a reference of this kind, or from background work that's still running when the reload starts. For more information about the .NET unloading requirements, refer to How to use and debug assembly unloadability in .NET (Microsoft).
A code reload leak affects your project in the following ways:
  • The memory that the Editor uses grows with every reload, because each leaked context keeps a full copy of your compiled code and its data.
  • The garbage collector needs to scan more objects, which slows down overall performance.
  • Code in a leaked context continues to run, so an event handler that's still subscribed or a thread that's still executing can make the same work happen twice.

Identifying leaks

The Editor reports a code reload leak a few seconds after a reload finishes, rather than during the reload itself.
The Console window displays a warning about leaked objects from unloading
UnityAssemblyLoadContext
instances.
The warning has the following form:
Found 1 leaked object(s) from unloading UnityAssemblyLoadContext instance(s)!Full leak detection report saved to: C:/MyProject/Logs/ALCLeaks.jsonNote: use '-record-managed-allocation-callstacks 1' cmdline argument switch to track callstacks for the leaked GCHandles and '-record-managed-allocation-callstacks 2' for GCHandles and objects.Leaked types:1 instance(s) of MyGame.AssetIndexRoot kinds:1 root(s) of kind Handle with None flags
The types under
Leaked types
are the objects that the Editor can't release. The entries under
Root kinds
describe what holds them.
The following are also symptoms of code reload leaks:
  • The memory that the Editor uses grows after each script change and doesn't return to its previous level.
  • A callback, event handler, or background task runs more times than you expect, and the number of extra runs grows with each script change.
For more information on interpreting the leak report Unity generates, refer to Assembly load context leak report reference.

Additional resources