Prefab references and memory

I work on a procedural game where the game objects are included as references in the script attached to a game object in a scene. As the game progresses, the game objects are instantiated from these references.

The problem is related to the amount of memory required to have all these references in the same scene. I am wondering if all the objects are loaded in memory ready to be instantiated just because there are references to the prefabs in the scene.

I created a test project to measure the memory required by the references without creating instances of the object references. I created an executable to run outside of the Unity editor to get accurate results.

The result showed that each build having more game object (prefab) references had more allocated memory. I cannot tell if the references took exactly the same amount of memory as the game object instances themselves. That creates a problem as the number of game object references increase, even though the objects are not instantiated.

I saw people using the “Resources” folder and the AssetBundle. That creates a heavier architecture to use the game object and I prefer to avoid that solution if there is a more convenient solution. Is it the only way to go or there are alternatives to minimize the memory impact?

1 Like

In C#, you have two types; value types and reference types.

If you have an array of GameObjects, they haven’t been instantiated, they don’t exist on the heap. The references, however, do. When you try to access one that doesn’t exist it will simply return null.

If you assign to it like this:

objectArray[0] = (GameObject)Instantiate(prefab, position, rotation);

Then the gameobject is put on the heap and the reference at objectArray[0] will point to it. Until then, no memory is allocated on the heap yet. The reason you saw the increase in memory allocation was because you’re storing the references as well as Unity, which internally keeps track of every single GameObject instance in the scene. If you instantiated all of those objects and assigned them to the array, you’d see the memory allocation jump up again.

What you write is the way I assume it works: prefabs are just regular gameObjects which happen to not be in the Scene. They’re nearly identical to inactive Instantiated objects. That’s why you’re allowed things like prefab.GetComponent. Instantiate is really just a smart copy command, which also adds the new item to the Scene.

But I don’t see that’s really a problem. Standard memory management will notice if you haven’t touched them in a while and move them into low-speed memory. Or I suppose you could try setting your pointers to them to null.

The alternative is writing code to build them yourself using AddComponent. Instead of prefabs, you’d store various values describing how to build it. That would also take memory, but maybe less (and your code would take memory as well.) But that’s a real way people have done things. I’ve even used a mixed approach, where some data tells me how to build the final gameObjects by combining and changing a few prefabs.

Thanks guys. I did some more tests to measure the memory. I created more builds with different parameters. So far, I have not been able to measure an increase in memory when using instantiated objects.

I created a texture of about 20 MB linked to a material. The material was placed in the script object in the references. That increased the build size on the disk. I also created a cube with the material and the result was the same. I am really wondering how the memory is tested. I used “Advanced FPS Counter” from the Asset Store that also monitor the memory.

I presume that this asset does not monitor every part of the memory. I can see the visual being different with instantiated objects on the screen, but I just cannot measure where the objects’ memory is stored. However, I measured that adding about 30 references increased the allocated memory of nearly 3 MB according to “Advanced FPS Counter”. I find this increase quite a lot for so few prefabs.

I am still wondering if using Resources.Load() would be a better deal. Other people mentioned that using the resources folder was the way to go when instantiating procedurally. Even though the memory allocated is smaller than the prefab size, I plan over 100 or 200 prefab references. That could be tens of MB in extra just for a prefab list. It is still considerable if porting to other platforms than PC.

Of course it wont grow with a texture when you instantiate it, because that’s exactly what you’ve done, instance it! ;p

Textures are stored in one place, when you create a new object it’s pointing back at that same texture, why create a new one?

For memory to shoot up it needs to be from new variables placed on the heap, in their own space in RAM, without any instancing going on. For that, you can try this:

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

public class Controller : MonoBehaviour {
    public GameObject prefab;
    public Text text;
    int numObjects = 0;
    List<GameObject> references;
   
    void Start () {
        references = new List<GameObject>();
    }
   
    void Update () {
        if(Input.GetKeyDown(KeyCode.Escape))
        {
            Application.Quit();
        }
        if(Input.GetKeyDown(KeyCode.A))
        {
            // Add object if list hasn't reached capacity
            if(numObjects < references.Capacity)
            {
                references.Add(Instantiate(prefab, new Vector3(Random.Range(-20, 20), Random.Range(-15, 15), 0), Quaternion.identity));
                numObjects++;
                text.text = "Reference list now size " + references.Capacity + ", objects:" + numObjects;
            }
            else
            {
                text.text = "Couldn't add object. Reference list now size " + references.Capacity + ", objects:" + numObjects;
            }

        }
        if(Input.GetKeyDown(KeyCode.R))
        {
            // Add reference
            references.Capacity += 1;
            text.text = "Reference list now size " + references.Capacity + ", objects:" + numObjects;
        }
    }
}

Add that to your scene, with a reference to a prefab that has this script on it:

using UnityEngine;

public class BigString : MonoBehaviour {

    private string s = "123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890";
   
    void Start () {
        //Creates a 4mb string
        for(int ii = 0; ii < 11; ii++)
        {
            s += s;
        }
    }
}

In start the string doubles in size with each pass of the for loop, initially it’s 1000 characters, which means after 11 passes it will be 2048000 characters long, giving a 4mb size.

Each time the list is expanded in capacity (press R to add a reference), it only grows by a few bytes. When you actually create the object (press A), Unity’s allocated memory will jump noticeably. You don’t even need to put it into a build, just ctrl + shift + esc for Task Manager and view the memory for Unity Editor (unless you’re on Mac of course).

1 Like