Search Unity

  1. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Question Why won't my Dict<Key, List<GameObject>> values NOT being destroyed?

Discussion in 'Scripting' started by Rickywild, Nov 2, 2023.

  1. Rickywild

    Rickywild

    Joined:
    May 20, 2015
    Posts:
    52
    Good Evening all,

    I am struggling with freeing up some memory here. I've recently began looking into using Addressables. In my project, I generate the level platforms from a bunch of different prefabs representing them.

    I store the loaded AssetRefences into into a Dictionary and it looks like this,
    Code (CSharp):
    1. public Dictionary<PlatformID, List<GameObject>> _platformObjectsDict;

    When it comes to unloading, I'm wanting to ensure the gameobject clone instances stored away in the dict are destroyed but it doesn't appear to be destroying them. I do also dict.clear() but that doesn't destroy all the lists containing the gameobject instances within them, right?

    Here's what i'm doing,

    Code (CSharp):
    1.             for (int i = 0; i < _maxPlatformObjs; i++)
    2.             {
    3.                
    4.                 Destroy(_platformObjectsDict[PlatformID.Platform_Forest_Ground_Earth][i].gameObject);
    5.                 Destroy(_platformObjectsDict[PlatformID.Platform_Forest_Ground_Earth_B][i].gameObject);
    6.                 Destroy(_platformObjectsDict[PlatformID.Platform_Forest_Ground_Stone][i].gameObject);
    7.  
    8. //ect
    9.                
    10.             }
    I've also tried the following but this also doesn't appeat to Destroy the game objects.

    Code (CSharp):
    1.             foreach (KeyValuePair<PlatformID, List<GameObject>> value in _platformObjectsDict)
    2.             {
    3.                 //List<GameObject> list = value.Value;
    4.                 foreach (var i in value.Value)//list)
    5.                 {
    6.                     Destroy(i.gameObject);
    7.                 }
    8.                 //Destroy(val);
    9.             }
    When I look at the contents of the dictionary' list gameobjects after the Destroy methods are called, I see that they're still populated? What does this mean? Are they just references to objects that no longer exist? i'd have thought they'd be removed.

    Need some help on this one.
     
  2. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    5,078
    This is a vague statement. Not appearing to ... means what exactly? Is the game object removed from the scene? Then yes, it has been destroyed.

    Destroying a game object won't automatically set it to null. You need to to both: destroy the objects, then clear the list or dictionary, or remove or assign null to individual entries in the collections.

    Note that destroying and null'ing game objects will have a minor memory usage impact. It's the resources that consume most memory, such as meshes, materials, textures and so on. There are methods to RemoveUnusedAssets (something to that effect).
     
    Rickywild likes this.
  3. Rickywild

    Rickywild

    Joined:
    May 20, 2015
    Posts:
    52
    It is a vague statement, fair point.

    I do this just before a scene change, so it isn’t easy to capture that. You’ve kind of answered my question towards the end of your response. What I’m trying to achieve is freeing up memory for the next scene.

    Eventually between scene changes,I’ve noticed memory build up in the profiler. It gets to the point when the next scene will not load,task manager reports not responding. This is because my RAM is taking a battering.

    Im using Addressables.ReleaseInstance on the AsyncOperatuonHandles appropriate to the AssetReferences. However I’m not seeing the memory drop and raise in between scene changes. Ie loading and unloading generating the level.

    I have a sneaky suspicion that all the cloned platform prefabs are remaining in memory I’m between scene loads and stacking up. These prefabs have material meshes on them.
     
  4. spiney199

    spiney199

    Joined:
    Feb 11, 2021
    Posts:
    7,315
    I'd take a read of this: https://docs.unity3d.com/Packages/com.unity.addressables@2.0/manual/MemoryManagement.html

    Long story short, assets aren't unloaded from memory unless the whole addressables group has been unloaded. Also the behaviour whilst in the editor isn't indicative of a build.

    Not sure if I'd worry too much about such granular asset loading/unloading. Worth remembering addressables loads any assets that are dependencies of an asset being loaded, which includes scenes, and vice versa. So most often the only thing I'm loading/unloading via addressables is scenes, and just let the system manage the dependencies. Then you just need to ensure all relevant assets are in addressables groups to prevent duplication (which often means all assets).
     
    CodeSmile likes this.
  5. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    5,078
    In that case you needn't destroy the game objects. Changing the scene does that for you .. normally. Of course you could still have references to those objects somewhere, for example if said dictionary was static or in a singleton class. If that is the case, you have a memory leak of sorts where you hold on to game objects from another scene. Accessing those objects' properties or methods should throw a MissingReferenceException though.

    Assets are something else entirely, and since game objects reference assets, they can keep assets in memory / prevent them from being unloaded. But as spiney pointed out, the assets won't get unloaded automatically.

    If you destroy a game object that hold the only reference to an asset and the next thing you do is to try and "unload unused assets" this will often NOT unload that game object's asset references because destroying objects is not immediate but deferred until the end of the frame (or start of the next frame). It is helpful for memory intensive games to load a "loading" scene, in which you then perform any cleanup, and after that's done you start loading the next scene to ensure unused memory is freed up before the next memory-heavy scene loads. And if you load it async, you can even animate the "loading" screen.

    There is also a Memory Profiler packages, use that to narrow down where the excess memory usage is coming from.