Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

WARNING: memory leak

Discussion in 'Scripting' started by Nevermind, Oct 3, 2012.

  1. Nevermind

    Nevermind

    Joined:
    Jun 10, 2010
    Posts:
    66
    I've just found a nasty memory leak scenario in Unity. I've submitted a bug, bt I'm also posting this thread as a warning to fellow developers.

    Here's what happens: suppose you have a script that references some assets - prefabs, textures, whatever. This script is attached to an object in a scene. When this scene is unloaded, assets referenced by this object are unloaded too - provided, of course, that they're not referenced somewhere else.

    BUT if you store a reference to this object somewhere in code, then assets would NOT get unloaded! The object itself is destroyed on scene change, and you can't access it from code - any attempt to use the reference causes an exception. However, with Resource.FindObjectsOfTypeAll() method you can enumerate all assets currently loaded, and these assets from now-destroyed object would still be there.

    I've made a simple project to test this - it's attached to the post. In it, there are three scenes. One is starting scene, and two others contain each an object that references a texture. The only difference is that "failScene" stores a reference to the object that in turn references the texture. When you return from this scene back to starting, and try to find the texture, it'd be there, and even displayed in OnGUI!

    You'd have to build an actual project to test this, because in editor the texture seems to be loaded all the time right from the start. But when I checked with Windows .exe build, there was the memory leak: texture got unloaded after visiting "okScene", but not after visiting "failScene".
    So be wary of storing references to objects after they are destroyed: it may seem harmless, but in fact causes memory leaks!

    I've tested it all on 3.5.6. Don't know if it happens on Unity4 beta, but I wouldn't be surprised if it in fact does.

    Submitted bug report number for anyone who is interested is an 492524
     

    Attached Files:

  2. MihaPro

    MihaPro

    Joined:
    Dec 23, 2010
    Posts:
    13
    Use code in TextureScript:
    Code (csharp):
    1.  
    2.     void OnDestroy()
    3.     {
    4.         Instance = null;
    5.     }
    6.  
     
  3. misterkid

    misterkid

    Joined:
    Feb 15, 2012
    Posts:
    79
    Long long time ago when I used javascript. And made a tower defanse game for my phone I had memory leak problems as well.

    I fixed it by doing this.
    Mesh wouldn't get destroyed.
    I don't think it is the same problem but I do think it is a memory leak bug :p
     
  4. JohnnyA

    JohnnyA

    Joined:
    Apr 9, 2010
    Posts:
    5,041
    EDIT: Looked at code.

    Your code is retaining a static reference to the texture. The texture is retained because you specifically asked for it to be retained.
     
    Last edited: Oct 9, 2012
  5. Nevermind

    Nevermind

    Joined:
    Jun 10, 2010
    Posts:
    66
    No, my code is retaining a static reference to a MonoBehaviour, not texture. The MonoBehaviour does reference the texture, but it's destroyed!
     
  6. JohnnyA

    JohnnyA

    Joined:
    Apr 9, 2010
    Posts:
    5,041
    You retain a reference to an instance of TextureScript which in turn holds a reference to the Texture. Therefore the texture is still referenced and so not destroyed. Static variables are members of the class not an instance (another name for them is class variables), they do not get destroyed when a scene is destroyed.

    This is not a bug; by their very definition they last for the lifetime of the application.

    It also happens that using static variables (via a singleton pattern) is a great way to store common data like game state or scores as it it can be shared between scenes.
     
    Last edited: Oct 9, 2012
    Stamblew likes this.
  7. Nevermind

    Nevermind

    Joined:
    Jun 10, 2010
    Posts:
    66
    I have a reference to TextureScript, that in turn has a reference to texture - that much is true. However, the referenced TextureScript object is destroyed on scene end; and I CANT deference it afterwards! Trying to use Instance.Texture would throw an exception saying that the object has been destroyed. I can see the logic behind retaning the reference, but then why it can't be accessed? Even better question is - ok, suppose I want to destroy this referenced texture explicitly... how can I do that?
     
  8. JohnnyA

    JohnnyA

    Joined:
    Apr 9, 2010
    Posts:
    5,041
    TextureScript.Instance is referencable.

    If you set it to null before scene end the texture is cleaned up fine. If you set it to null after the scene and either load another scene or do a Resource.UnloadUnusedAssets the texture is also cleaned up fine.
     
    Last edited: Oct 9, 2012
  9. Nevermind

    Nevermind

    Joined:
    Jun 10, 2010
    Posts:
    66
    Could you please show me any way to access this referenced texture after scene is unloaded? Except scanning all assets for it, as my exmple does?
     
  10. JohnnyA

    JohnnyA

    Joined:
    Apr 9, 2010
    Posts:
    5,041
    Code (csharp):
    1.  
    2. texture = TextureScript.Instance.Texture;
    3. /*
    4.             var textures = Resources.FindObjectsOfTypeAll( typeof( Texture2D ) );
    5.             texture = textures.Cast<Texture2D>().FirstOrDefault( t => t.name == "texture" );
    6.             if( texture != null )
    7.             {
    8.                 Debug.Log( "GOTCHA!" );
    9.                 Debug.Log( "TextureScript.Instance==null is " + (TextureScript.Instance == null) );
    10.             }
    11. */
    12.  
    Works for me.
     
  11. Wozik

    Wozik

    Joined:
    Apr 10, 2009
    Posts:
    662
    The answer is sponsored by Unity Core team:

     
  12. Nevermind

    Nevermind

    Joined:
    Jun 10, 2010
    Posts:
    66
    OK, sorry, I was wrong, it does work. What I don't understand is...

    WHY?! First you go to great pains to make all destroyed objects look like null's, and throw exceptions on any access, and then you turn around and say "but actually, your script is not unloaded, it's only kinda invalid, but not wholly..." That might be intended behaviour, but it is surely inconsistent!

    To clarify, this doesn't have anything to do with static variables. Whenever I retain a reference to a destroyed MonoBehaviour, any assets referenced by that MonoBehaviour are not unloaded, even though my reference is, or pretends to be, null.
     
  13. Kim-Riber

    Kim-Riber

    Unity Technologies

    Joined:
    Feb 25, 2011
    Posts:
    25
    Hi.

    When doing a singleton manager like the one in your script:

    Code (csharp):
    1.  
    2. public class TextureManagerScript : MonoBehaviour
    3. {
    4.     public Texture Texture;
    5.     public static TextureManagerScript Instance;
    6.  
    7.     void Start()
    8.     {
    9.         Instance = this;
    10.     }
    11.  
    12.     void OnDestroy()
    13.     {
    14.         Instance = null;
    15.     }
    16. }
    17.  
    It is important to clear the instance when the Singleton is destroyed.

    The issue is that the MonoBehavior under the GameObject is a component with both a c++ part and a mono part.
    The c+ side of the MonoBehavior is destroyed when unloaded. The c# part is not since there is still a c# reference to the MB from the static variable that lives in mono.
    When accessing the MB from script later on, this can be done in 2 ways. If checking equality, this is going through c++, and since the c++ part is gone, this returns null.
    If on the other hand you go directly to Instance.Texture, you will not enter c++ land, and you will get the texture that is statically referenced.

    This is of course not the best solution, but we have not found a way to solve this yet.

    Hope this explains a bit of what is going on under the hood.
     
    Stamblew likes this.
  14. manny003

    manny003

    Joined:
    Mar 18, 2014
    Posts:
    69
    OK. I've read the entire thread very carefully and I still don't understand one simple thing -- Isn't the Garbage Collector supposed to prevent all of this? Isn't it supposed to clean up all unreferenced objects?

    I'm running into a app crash situation where I'm switching back and fourth between scenes where, after a while, the whole thing crashes. None of the scenes have any static objects. It does however, has two static primitives (int, float).

    Currently using Unity 4.5.2f1 Standard with a Pro trial license.

    Manny
     
  15. Randyanto

    Randyanto

    Joined:
    Dec 30, 2013
    Posts:
    3
    I have tried this in Unity 4.6.
    Should I add
    Code (csharp):
    1.  Instance.Texture = null;
    at the
    Code (csharp):
    1.  OnDestroy()
    ?
     
  16. Randyanto

    Randyanto

    Joined:
    Dec 30, 2013
    Posts:
    3
    STATIC variables won't be touched by the GC because STATIC variables are supposed to be there as long as the application is alive
     
  17. Jonny-Roy

    Jonny-Roy

    Joined:
    May 29, 2013
    Posts:
    666
    Agreed, this is how it should work.

    It's the same situation with any idisposable objects too. for example:

    Code (CSharp):
    1. StreamReader sr = new StreamReader("TestFile.txt");
    2. // Read the stream to a string, and write the string to the console.
    3. String line = sr.ReadToEnd();
    4. Console.WriteLine(line);
    5. //This will destroy the underlying StreamReader...but the variable will still exist.
    6. sr.Dispose();
    It's not a Unity bug, it's how programming works. Instead of complaining about it, learn about it. In my example, the StreamReader is destroyed, but the variable still references it and so can still access it's properties. sr will only be Garbage Collected once it goes out of scope, so as a local variable, once the method ends it goes into the Garbage Collector, if it was a static variable though, then it will only lose scope when it's set to null or the application ends. Simple.
     
  18. Stamblew

    Stamblew

    Joined:
    Sep 3, 2014
    Posts:
    22
    After 7 years from the first post of this thread, I could understand and profile my game to find out that not setting the static Singleton instance to null was causing many memory leaks. This is especially crucial in mobile devices. Thanks for all the info provided here, folks!:)
     
  19. PigBang

    PigBang

    Joined:
    Sep 19, 2016
    Posts:
    7
    You also can catch this when use actions and lambda functions.
    For example:

    Code (CSharp):
    1.         private static Action testAction;
    2.  
    3.         [SerializeField]
    4.         private GameObject testField;
    5.  
    6.         private void TestFunction() { }
    7.  
    8.         protected void Awake()
    9.         {
    10.             // If you miss unsubscription - you has leak
    11.             testAction += TestFunction;
    12.  
    13.             // While AnyClass use Action(with class fields) - you has leak
    14.             AnyClass.SomeFunction(() =>
    15.             {
    16.                 testField.SetActive(true);
    17.             });
    18.             // or class functions
    19.             AnyClass.SomeFunction(TestFunction());
    20.         }