Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more

GC, Destroy, and Dispose are not interchangeable ways to “free memory” in Unity. Each one ends a different kind of lifetime. The GC reclaims managed C# objects that nothing references anymore. Object.Destroy removes a Unity object’s native counterpart. Dispose releases unmanaged memory that your code explicitly allocated, such as a NativeArray<T>. Before choosing one, ask two questions: who owns this memory, and what lifetime should end?

Start with ownership and lifetime

Most memory confusion in Unity comes from treating a C# variable as if it were the whole object. In practice, a single gameplay object can involve several layers at once: a managed C# wrapper, a native Unity object the engine keeps internally, and perhaps unmanaged buffers your own code allocated. Each layer has a different release mechanism.

Unity’s Scripting API for UnityEngine.Object describes the link directly: “Each instance of a class that derives from UnityEngine.Object is linked to a counterpart native object” (Unity Technologies, Unity Scripting API: Object, Unity 6.0 documentation: https://docs.unity3d.com/cn/6000.0/ScriptReference/Object.html). That single sentence explains why the three mechanisms differ.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Mechanism Applies to What it releases What you do
Garbage collection (GC) Managed C# objects, collections, and strings with no remaining references Managed heap memory Drop references you no longer need. The collector reclaims the objects later, at a time you do not control.
Object.Destroy GameObject, Component, and other Unity objects that should stop existing in the scene The Unity object and its native counterpart Call it when the Unity-side object should be removed. The C# wrapper is then collected by the GC once nothing references it.
Dispose Explicitly allocated unmanaged containers such as NativeArray<T> The container’s native allocation and its safety bookkeeping Call it when the allocation’s intended lifetime ends, or pass a JobHandle so disposal happens after scheduled work finishes.
Resources.UnloadUnusedAssets Loaded Unity assets that Unity considers unused Native asset data for unused assets Call it at a deliberate transition, such as between scenes, and only after confirming no references keep assets alive.

Managed objects: let the GC reclaim them

Unity’s GC reclaims managed objects that are no longer reachable. It is not a delete call you make for each object. A plain C# class, a List<T>, or a string built during gameplay becomes eligible for collection once no live reference points to it. Your job is to avoid keeping unnecessary references alive, such as entries in a static list that are never removed, or event subscriptions that were never unsubscribed.

Do not call GC.Collect as a routine cleanup step. A forced collection does work immediately, and that work can cost CPU time at exactly the moment a frame is already under pressure. Unity’s garbage collector overview (Unity Technologies, Garbage collector overview, Unity 6.0 Manual: https://docs.unity3d.com/ja/6000.0/Manual/performance-garbage-collector.html) is the right reference for how collection is scheduled and how allocation pressure shows up in a profile.

The GC also does not manage native memory. Unity’s managed memory introduction states it plainly: “The garbage collector doesn’t clear out native memory objects or other native allocations” (Unity Technologies, Managed memory introduction, current manual: https://docs.unity3d.com/jp/current/Manual/performance-managed-memory-introduction.html). That is the main reason a collection cycle does not reduce native memory use, and why Destroy and Dispose exist.

Unity objects: call Destroy when the object should go

Object.Destroy removes the Unity object. It does not directly free the managed wrapper the moment you call it. Three behaviours matter in practice:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  • Timing. Destruction takes effect at the end of the current frame’s update cycle, not at the line where you call it. Code that runs later in the same frame can still see the object.
  • Null checks. After a Unity object is destroyed, comparing it to null returns true, because Unity overrides the comparison. A plain C# reference you still hold is not automatically cleared, and the wrapper stays in memory until nothing references it.
  • Immediate removal. Object.DestroyImmediate removes the object at once. It is meant for editor scripts and special cases, not normal runtime gameplay code. Using it during a frame can produce unpredictable ordering with other scripts.

Destroying a parent GameObject also removes its children and their components, so you rarely need to destroy each child separately.

Native containers: Dispose what you allocated

NativeArray<T> stores its data in unmanaged memory. The GC will not reclaim that memory, and no collection cycle will free it. You must dispose the container when the allocation is no longer needed. Unity’s API page for NativeArray<T>.Dispose describes both a direct call and a form that accepts a JobHandle (Unity 6.3 API content, hosted on a documentation mirror: https://w.swordxu.com/Unity6000.3/en/ScriptReference/Unity.Collections.NativeArray_1.Dispose.html; the class overview is at https://swordxu.com/Unity6000.3/en/ScriptReference/Unity.Collections.NativeArray_1.html). Because this is a mirror rather than Unity’s own domain, check the signatures against the documentation installed with your editor before copying code.

Choose the allocator first

The allocator determines how long the memory is meant to live. Pick it when you create the container, because it decides how strictly you must dispose it.

Allocator Intended lifetime Disposal expectation
Allocator.Temp Very short, within the current frame Dispose before the frame ends
Allocator.TempJob Short-lived work, such as a job that completes within a few frames Dispose once the consuming job has finished, typically through the JobHandle overload
Allocator.Persistent Long-lived data that outlives a single frame or job Dispose explicitly when your system shuts down or the data is replaced

These lifetimes reflect Unity’s allocator model as documented for the collections package. Confirm the exact allocator behaviour in the documentation for your installed Unity 6.3 version.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Dispose after the job that uses it

If a job reads or writes a NativeArray, disposing the array on the main thread before that job finishes is a bug. Pass the job’s handle to Dispose so the disposal waits for the job:

using Unity.Collections;
using Unity.Jobs;

var data = new NativeArray<float>(1024, Allocator.TempJob);
JobHandle handle = job.Schedule(data.Length, 64); // job: your IJobParallelFor struct that uses data
data.Dispose(handle); // frees data only after the scheduled job completes

Do not read or write data on the main thread after the call. Complete the handle yourself if you need results immediately, then dispose.

Symptoms of a missing or early Dispose

  • Leak warnings in the Console. If the editor reports a native collection that was not disposed, the allocation has no owner releasing it. Add disposal at the end of its owner’s lifetime.
  • Safety exceptions. Disposing or accessing a container while a job still depends on it triggers Unity’s job safety checks. Fix the dependency chain rather than suppressing the check.
  • Memory that grows over a session. A Persistent allocation created on every level load and never disposed accumulates across loads.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Unused assets: Resources.UnloadUnusedAssets

Resources.UnloadUnusedAssets unloads loaded assets that Unity considers unused. Its name suggests a general cleanup, but it does not run the GC, and it does not dispose managed objects. It operates on asset and native object data.

Two conditions often surprise developers. First, an asset stays loaded if something still references it, including a static field, a singleton, a cached component, or an event subscription. Second, the call is not cheap, so it is usually placed at a scene transition or loading screen instead of during active gameplay. If you use Addressables or your own loading system, the matching release call in that system is the one to use for those assets.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Decision guide

  1. Is it a plain managed object or collection? Remove the references you hold when you finish with it. Let the GC reclaim it. Avoid GC.Collect unless profiling shows a specific, measured benefit.
  2. Is it a Unity object that should disappear from the scene? Call Object.Destroy. Do not expect it to release unrelated managed or native memory.
  3. Is it a NativeArray<T> or another explicitly allocated native container? Choose the allocator by lifetime, identify its owner, and dispose it at the end of that lifetime. If a job uses it, pass the job’s JobHandle to Dispose.
  4. Are loaded assets no longer needed? Remove their references first, then consider Resources.UnloadUnusedAssets at a deliberate transition point.
  5. Do you see GC spikes or allocation pressure? Profile the build on the target platform before changing anything. Reducing allocations is usually the first fix.

GC modes and incremental collection

Incremental GC spreads collection work across several frames instead of doing it in one long pause. It can reduce the size of a single stall, but it does not remove all frame-time spikes, and it adds its own overhead. Unity’s garbage collector overview covers the trade-offs (see the link above). In the Unity 6 editor, the setting is found under Project Settings > Player > Other Settings > Configuration as “Use Incremental GC”; confirm the label in your installed version. Changing the GC mode or disabling collection is a performance decision that should follow profiling. Disabling collection also demands strict control over allocations and object lifetimes, or memory grows without bound.

Version and platform limits

The distinctions above come from Unity’s documentation for the managed heap, the Object class, and the native collections API. Unity 6.3 API pages for NativeArray<T> were reached through a documentation mirror, so use the editor’s installed documentation as the authority for exact signatures. Backend-specific and platform-specific behaviour, including differences between Mono and IL2CPP builds and console or mobile targets, can change allocation and collection timing. Test on the platform you ship.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.