Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Voting for the Unity Awards are OPEN! We’re looking to celebrate creators across games, industry, film, and many more categories. Cast your vote now for all categories
    Dismiss Notice
  3. Dismiss Notice

Unreferenced Scriptable Objects are not being Garbage Collected

Discussion in 'General Discussion' started by jeango, Oct 15, 2018.

  1. jeango

    jeango

    Joined:
    Dec 19, 2012
    Posts:
    106
    Hi all,
    Not sure in which part of the forum this belongs, so I post in general.

    I've been looking up on scriptable object lifecycles, trying to understand what happens to a scriptableobject instance once it's no longer referenced.

    I have seen multiple posts in answers saying that unity will garbage collect them, and that it will not result in a memory leak, but I had my doubts so I wrote this simple script:

    Code (CSharp):
    1.  
    2. public class SOLifeCycleTester : MonoBehaviour {
    3.  
    4.     public TestSOLifecycle assetLifeCycle;
    5.     public TestSOLifecycle runtimeLifeCicle;
    6.  
    7.     public int runtimeLifeCycleCount;
    8.  
    9.     public void Start()
    10.     {
    11.         print(Resources.FindObjectsOfTypeAll(typeof(TestSOLifecycle)).Length + " SO still loaded");
    12.     }
    13.  
    14.     public void Update()
    15.     {
    16.         if(Input.GetButtonDown("Fire1")) {
    17.             runtimeLifeCicle = ScriptableObject.CreateInstance<TestSOLifecycle>();
    18.             foreach (var item in FindObjectsOfType<TestSOLifecycle>())
    19.             {
    20.                 print(item.name + "is still loaded and active");
    21.             }
    22.             runtimeLifeCycleCount++;
    23.             runtimeLifeCicle.name = "runtimeSOLifeCycle" + runtimeLifeCycleCount;
    24.             if (runtimeLifeCycleCount == 5) {
    25.                 System.GC.Collect();
    26.             }
    27.             if (runtimeLifeCycleCount == 10)
    28.             {
    29.                 SceneManager.LoadScene(SceneManager.GetActiveScene().name);
    30.             }
    31.  
    32.         }
    33.     }
    34. }
    35.  
    Where TestSOLifecycle is a scriptableobject that prints a message when it's enabled, disabled or destroyed

    If the objects were properly destroyed, I shouldn't ever have more than 1 scriptable object when I do a FindObjectsOfType (because the asset-type instance is not returned when doing a findobjectsoftype)

    But instead I can clearly see from running my test that unreferenced SO are NEVER garbage collected.

    When the scene is re-loaded, OnDisable is called on all instances, but not OnDestroy. FindObjectsOfTypeAll().Length is 1 at scene reload, so I assume the objects were indeed Garbage Collected

    So what I'm assuming happens is unity keeps a list of all active objects, so as long as the SO is still active, there IS still a reference to it internally and GC will not touch it.

    In short, always always always make sure you explicitly destroy a scriptableobject instance that was created at runtime and no longer referenced by any of your objects (how you figure that out is up to you). Don't rely on garbage collector to do that work for you.
     
  2. superpig

    superpig

    Drink more water! Unity Technologies

    Joined:
    Jan 16, 2011
    Posts:
    4,613
    There are two garbage collectors in Unity:

    * The Scripting garbage collector (GC.Collect)
    * The Unity Object garbage collector (Resources.UnloadUnusedAssets)

    Unreferenced SOs should be cleaned up by the second collector. They will not be cleaned up by the first.
     
  3. jeango

    jeango

    Joined:
    Dec 19, 2012
    Posts:
    106
    Well perhaps they SHOULD but they don't according to my test. You can run my script and see for yourself
     
  4. jeango

    jeango

    Joined:
    Dec 19, 2012
    Posts:
    106
    Nevermind, I replaced the GC.Collect with UnloadUnusedAssets and indeed they get collected. I guess I was expecting that GC to run more often
     
  5. JJJohan

    JJJohan

    Joined:
    Mar 18, 2016
    Posts:
    214
    Also of note is that UnloadUnusedAssets actually invokes GC.Collect as well, at least based on this response from 2015. It's a bit dated but I haven't found any updated response saying this behaviour has changed. Unfortunately it's not mentioned in the documentation.