[Check the original post at Unity Memory Management: Unload That Asset!]
I have a question for you regarding Unity memory management; when exactly is Unity releasing the memory your game assets are taking?
Most game developers wrongly assume it's released when you stop using them (whatever using means).
But... This assumption is wrong.
Let's find out why.
In this post, I'll share with you:
- How most game developers (wrongly) assume their game assets are unloaded as soon as they stop using them
- The negative consequences of assuming so
- When exactly your assets are unloaded if you're NOT using addressables
- And if you do use addressables: when are your addressable assets truly unloaded?
There's a reason this topic is relevant.
Here's what I see in most profiles: an increasing amount of used RAM and a confused developer asking me why.
"That must be wrong, I'm destroying my game object instances", they tell me.
And here's what I usually answer: "sorry, destroying instances won't necessarily get your memory back".
If you're aware of this fact, this issue will rarely be a concern.
But if you're not, then you might run into confusion and/or trouble.
Let's see why.Quick Navigation (redirects to new tab)â€‹
Unity Memory Management: Asset Unloading Assumptions
Here's a typical case:
- You load a scene.
- You spawn a few enemies from a prefab.
- Your player kills them and then you destroy all these instances. You even go as far as to remove the original reference to the prefab to make sure it's removed from memory.
- The player keeps playing. However, the memory taken by that prefab didn't come back and your internal dialog says "shitty editor".
- You additively load a scene that contains the pause menu UI. All its sprites are loaded, and that's fine.
- The user returns to the game and you unload that additive scene.
- You profile again and aren't happy to see the sprites are still in memory.
And the last one: you call Addressables.Release(myInstance) on an addressable asset. You're furious to see that nothing changed in your memory layout.
"What the f*** is going on here?", you curse aloud.
These all are examples of wrong assumptions most game developers make regarding Unity memory management and asset unloading.
And these assumptions might have somewhat nasty consequences.
Consequences of Wrong Assumptions
If you believe memory is released when it is NOT, then you might run into trouble at some point.
You'll think you have more free memory than you actually have.
→ This won't endanger your game often, as a good operating system will kindly remind Unity to clean its shit when it becomes too greedy. But some OS won't be that forgiving when it comes to memory (Oculus Quest comes to mind).
However, your mental sanity is at risk.
If you ever played Arkham Horror, or Eldritch Horror for the matter, you'll know better than to play with sanity points.
→ Here's the second problem: profiling your game won't be accurate anymore because the numbers you see won't match what you expect.
This will lead to questions such as "where are these extra 200MB coming from?" or "why is this still in memory even if I destroyed all references to it?". Even worse, you could end up asking yourself why are you in gamedev after all.
Okay, calm down.
We want both our gameplay and our profiling experiences to be silk smooth, right?
We don't want to hear the iron-like fist of our boss striking your desk asking about those kickstarter users complaining about crashes.
All good. I'll help you understand when exactly Unity unloads your assets and how to help it do it sooner.
I'll split the explanation in two sections:
- When are your assets unloaded if you're NOT using addressables — i.e. using the traditional Unity memory management
- When addressables will unload your assets if you do use it
Traditional Memory Management: Releasing Memory
Basically, Unity wants it safe for the game developers. And so it adds protection.
Unity is over-protective of memory and therefore under-performing.
I don't say this is wrong, as this saves you from taking aspirin. But they could be more transparent with memory management so everybody is aware of what's going.
If you're not using addressables but the traditional memory management flow, Unity follows a specific memory release pattern: Unity releases your assets' memory during the cleaning process and only if your asset meets the cleaning requirements.
This is when the memory cleaning process takes place:
- When you manually call Resources.UnloadUnusedAssets or any of its variants. This operation is slow and will block the game thread, so your users will definitely notice a hitch.
- When you load a scene non-additively — i.e. you replace a scene through the single mode
- Whenever else Unity finds it appropriate — e.g. when Unity receives a memory warning from the O.S.
And this is the basic requirement for your asset to be freed: your asset must NOT have any live reference pointing to it at the moment of cleaning.
These asset references exist in components that live within a game object that is alive in your current scene hierarchies.
To sum up: it's hard to have efficient control over memory when using the traditional memory management workflow.
Luckily, you can always upgrade to Addressables to have finer control over memory as we're about to see.
Addressables Memory Management: Releasing Memory
Unity Addressables is a system that significantly upgrades the way you work with references.
The Addressables API lets you decide when to load and unload your assets even if you keep references to them. The traditional workflow doesn't give you this privilege.
You do these operations by calling the LoadAssetAsync and Release methods on specific addressable asset references.
Addressables makes memory management much more predictable for the professional game developer.
However, this is not without misconceptions.
You see, calling Release is not guaranteed to free your asset's memory.
Let me explain how this works.
- When you load an asset, you increase its reference count by 1. The reference count is just an integer that counts how many users this asset has. I show you the place in code on the code capture below (1).
- When you release an addressable asset, you decrease its reference count by 1, as shown on (2).
- And whenever the reference count reaches 0, the addressable asset is destroyed, as I show you in (3).
Unity Addressables: Reference Counting Code
Now, the meaning of destroying is different depending on the type of addressable asset you're talking about.
If we're talking about an addressable asset, then destroying it won't actually do much, other than messing with the internal cache of the addressables system. This means: releasing an addressable asset won't release its memory by itself.
But here's the thing: your addressable assets live within an asset bundle. And asset bundles also have their own reference count.
If the system detects that an asset bundle containing addressable assets has no live dependencies (e.g. addressable assets), then its reference count becomes 0 and the asset bundle is destroyed.
And that's the key: destroying an asset bundle is the operation that will truly free the memory of the addressable assets it contains.
To sum it up:
- You load assets from an asset bundle progressively. Each addressable asset you load will add to your memory footprint.
- All addressable assets from an asset bundle are unloaded at once. This happens when all of them stop being used.
Once you know this, it's very easy to be mindful about managing your memory.
You'll see things click. You'll understand why it's not a good idea to create huge addressable groups — i.e. asset bundles.
If you're suffering from this problem, you have to easy ways of relieving memory pressure:
- Split your addressable assets into multiple addressable groups in a way that they don't coexist with each other — e.g. grouped by level.
- Set your addressables group's Bundle Mode to Pack Separately instead of Pack Together. This will pack each item in a separate asset bundle so that each asset will always be unloaded on Release.
Now you have knowledge
Now you have control
Now you have work
The Switch to Addressables — The Guru's Way
As you can see, you barely have control over your own memory if you're still using the default Unity memory management system.
To avoid all this trouble and to make sure your game stays on top from development to release, it is important that you switch to the modern era of addressable assets.
The best way to start is to check out my Unity addressables tutorial. There, you'll learn why your game might need addressables and how to get started quickly.
However: if you're already suffering from nasty memory problems and you're in a hurry to get those sorted out, consider applying for any of my Unity coaching programs.
I hope this post helps you at some point during your career.