Search Unity

  1. Unity Asset Manager is now available in public beta. Try it out now and join the conversation here in the forums.
    Dismiss Notice

Bug Why is this code leaking memory?

Discussion in '2022.1 Beta' started by Rowlan, Jan 2, 2022.

  1. Rowlan

    Rowlan

    Joined:
    Aug 4, 2016
    Posts:
    4,290
    I have an editor window with a gradient that needs to be updated on certain changes. I noticed that it leaks memory. Moreover and what's worse is that the whole Unity menu system seems to break. When I click the same menu item over and over all of a sudden totally different editor windows appear instead of the one I clicked.

    I guess it might be just a side effect - which shouldn't happen either way. Regarding the memory leak here's a minimal example.

    Here's the code:

    Code (CSharp):
    1. using System;
    2. using UnityEditor;
    3. using UnityEditor.UIElements;
    4. using UnityEngine;
    5.  
    6. public class MemoryLeak : EditorWindow
    7. {
    8.     [Serializable]
    9.     public class MyData : ScriptableObject
    10.     {
    11.         public Gradient gradient;
    12.     }
    13.  
    14.     [SerializeField]
    15.     private MyData myData;
    16.  
    17.     private GradientField gradientField;
    18.  
    19.     [MenuItem("Testing/Memory Leak")]
    20.     static void Init()
    21.     {
    22.         EditorWindow editorWindow = GetWindow(typeof(MemoryLeak));
    23.         editorWindow.titleContent = new GUIContent("Memory Leak");
    24.         editorWindow.Show();
    25.     }
    26.  
    27.     public void OnEnable()
    28.     {
    29.         myData = myData == null ? ScriptableObject.CreateInstance<MyData>() : myData;
    30.     }
    31.  
    32.     public void CreateGUI()
    33.     {
    34.         gradientField = new GradientField( "Gradient");
    35.         gradientField.value = myData.gradient;
    36.         gradientField.bindingPath = nameof(myData.gradient);
    37.         rootVisualElement.Add(gradientField);
    38.  
    39.         SerializedObject so = new SerializedObject(myData);
    40.         rootVisualElement.Bind(so);
    41.     }
    42.  
    43.     public void Update()
    44.     {
    45.         // leak happens here
    46.         Gradient gradient = gradientField.value;
    47.         gradientField.value = gradient;
    48.  
    49.  
    50.     }
    51.     void OnDisable()
    52.     {
    53.         if (myData != null)
    54.         {
    55.             ScriptableObject.DestroyImmediate(myData);
    56.             myData = null;
    57.         }
    58.     }
    59.  
    60. }
    61.  
    When you open the editor window using Testing > Memory Leak you can see the memory rise in the task manager.

    For one the cause seems to be related to the assignment of a gradient in the Update() method. It also seems to be related to the bindingPath. What exactly causes this I'm actually not sure. Anyone got an idea?

    ps: A different matter is why copy/paste context menu on the gradient label doesn't work if bindingPath isn't set. But then the leak is gone and that's the lesser evil.
     
  2. Rowlan

    Rowlan

    Joined:
    Aug 4, 2016
    Posts:
    4,290
    I found the solution. I need to set bindingPath and invoke SetKeys on the gradient attribute of my ScriptableObject. The object of the GradientField mustn't be modified.

    I created an issue nontheless since I've wasted too much time on this: Case 1392051

    If re-assignment of fields causes leaks, the re-assignment must be prevented.
     
    LeonhardP likes this.