How to Rescue Memory on Disabled GameObjects

Published January 27, 2020 by Ruben Torres Bonet (The Gamedev Guru)
Do you see issues with this article? Let us know.
Advertisement

[Check the original post at Unity Save Memory Within OnDisable]

One of my students, Ryan, asked me last week how he could rescue the memory Unity was stealing from his game because of disabled game objects. Luckily, he was using Addressables, so I prepared an experiment...

In this post you'll learn:

  • When is it ok to have deactivated GameObject's
  • Why disabled GameObjects pose a threat to memory
  • How to get your memory back without destroying them


Ready?

With Unity, there are many reasons you might want to deactivate entire GameObject hierarchies in your scenes...

  • Maybe you don't want specific elements to be visible or executing yet.
  • Or maybe you're doing object pooling.

In any case, deactivating a game object effectively disables invoking the Update behavior.

And that's great, because deactivating a GameObject will save you precious CPU cycles.

However, it's not all perfect...

There's one thing we're not saving on when disabling objects: memory.

You see, keeping a direct reference to your assets will keep them loaded in memory. It doesn't really matter if they're invisible, disabled or whatever. Direct reference = memory greed.

But today I have a trick up my sleeve for you.

I'll show you how to recover the memory your disabled game objects are feeding on.

No worries, we'll make sure they get their memory back once they awake. We're not that selfish after all.

Quick Navigation (redirects to new tab)​

Chapter 1: A Typical Scenario: Wasting Memory

Chapter 2: An Alternative Scenario: Saving Memory

Chapter 3: Conclusion & Comparison

CHAPTER 1:
A Typical Scenario: Wasting Memory

In this chapter, I'll show you a simple Unity scene that contains a few referenced assets.

You'll see how they refuse to exit memory when we deactivate their objects.

As simple as it is, this represents a vast amount of examples I've seen across a multitude of projects.

So my guess is that you suffer from this in your project.

To make a case for the post, I created a simple Unity scene with the following scene hierarchy:

  • Pause Canvas
    • Image0 → Sprite0
    • Image1 → Sprite1
    • Image2 → Sprite2
  • SoldierCharacter → Static Mesh + 4 Materials

In this case, it doesn't always make sense to keep these objects active...

  • If you're not paused, there's no need to keep their sprites in memory.
  • If the soldier is pooled and waiting for its turn to spawn, it's better to keep it deactivated.

You might say: If I want the memory, I can just destroy it.

And you'd be right. You could destroy them to save memory.

However, some game architectures rely on persisting state within your objects. You might want to store the last parameters the pause menu had. Or you might want to save the customization parameters of that soldier that we will spawn after crossing that door.

No matter what, there'll be circumstances where you'll have deactivated objects for certain period. And it might be wise to save memory on those until the moment of the activation occurs.

Here's how the setup scene looks like (don't judge my art style, I'm a programmer)

Saving Memory OnDisable: Setup Scene

Saving Memory OnDisable: Setup Scene

As you can see in the GIF below, deactivating the whole hierarchy won't release a single bit of memory (actually I'm not sad about it, as this keeps me employed).

Unity Default Behavior: Memory Greed

Unity Default Behavior: Memory Greed

And that's the problem we are about to tackle in the next chapter.

CHAPTER 2:
An Alternative Scenario: Saving Memory

In the previous chapter, we've seen how deactivating game objects help reduce CPU usage. But it doesn't really help us reducing our memory usage.

In our example, we still pay memory for the following asset types:

  • Textures
  • Meshes
  • Sprites

In this chapter, we'll get rid of this memory cost in a sneaky way.

Alright...

We're going to use Addressables.

And the trick is going to be a relatively simple script.

From a high-level perspective, this is what we're going to do:

  1. We're creating a simple script that holds an indirect reference to the asset our original component is using. This could be an indirect reference to a sprite being used in a UI Image, or a material in a MeshRenderer or a mesh in a MeshFilter.
  2. We then remove the direct reference from the original component (Image, MeshFilter, MeshRenderer).
  3. The script will load the asset during OnEnable and assign it to the correct field.
  4. When we disable the object in OnDisable, we unload the content.

This approach simply works for a simple reason...

When someone decides your content must be visible, your game object will be activated. This, in turn, will call our script's OnEnable function where we will load the relevant asset.

The same applies in the other direction. When we're done with an object, we either disable or destroy it. But whatever function gets called is not relevant for us, as Unity calls OnDisable for us and there we'll get rid of that memory section.

In my example, I implemented this simple mechanism for 3 components:

  • MeshFilter: indirect reference to a Mesh
  • MeshRenderer: indirect references to Materials (and therefore textures)
  • UI Image: indirect reference to a Sprite

And you are just about to see the results of this script in the profiler...

Unity Addressables: OnDisable Memory Gain

Unity Addressables: OnDisable Memory Gain

Yeah, as I said, that just works.

And the good news is that you can extend this to other components and asset types such as SkinnedMeshRenderer and the like.

CHAPTER 3:
Unity: Save Memory - Conclusion & Comparison

So far, we've seen how Unity's default behavior doesn't encourage efficient memory usage patterns.

But we also saw a way to fight the battle and reclaim our memory.

A question that remains unanswered is... is this fight worth the price?

As you might have noticed, this solution isn't free of side-effects.

To use it, you must be willing to pay a price: latency.

Indeed, loading an asset asynchronously will often take several frames. Depending on the frame-rate you're targeting, it might be unnoticeable but there's always the chance you can spot the small delay.

However, often enough you'll just need the memory savings. This is especially true if you're targeting older Android devices.

And honestly, it's not always about needing. Every MB you save on memory will reduce the chances of crashes.

You never know what the user is running at any given time, so the less memory you use, the better off the reviews of your game will be.

To quickly recap what we achieved with this Addressables-based tool, here's what we got:

Unity Addressables: OnDisable Comparison

Unity Addressables: OnDisable Comparison

That's a respectable saving on this particular scene, I must say. Of course, this will immensely vary depending on your use case.

My suggestion?

Try it out in your game and let me know your thoughts.

This tool works by:

  1. Having a basic Addressables setup (check my tutorial on Unity addressables)
  2. Attaching and setting up Mesh_ManageMemoryLifecycle to your static mesh or Image_ManageMemoryLifecycle to your UI image.

Remember: the sprites, meshes and materials you want to target must be addressables-activated.

Here's how the components look like:

OnDisable: Memory Gains for Meshes

OnDisable: Memory Gains for Meshes

OnDisable: Memory Gains for UI Images

OnDisable: Memory Gains for UI Images

Neat, isn't it?

Grab the tool and the unity sample project to start getting memory gains today

Cancel Save
1 Likes 10 Comments

Comments

Nobody has left a comment. You can be the first!
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!

Featured Tutorial

Upset at Unity's greedy behaviour? Stop this engine from stealing your memory on disabled game objects. You save memory now.

Advertisement
Advertisement