Search Unity

Question C# objects are leaked but can't find them with Memory Profiler.

Discussion in 'Editor & General Support' started by craftx, Feb 28, 2021.

  1. craftx

    craftx

    Joined:
    Oct 2, 2018
    Posts:
    13
    We are investigating a memory issue in our game that the used mono heap size keeps increasing. To confirm what's leaked, I have added a counter for a key C# object and displayed its count in a debug UI in the game. When playing the game, we do see the object count keeps increasing along with the used mono heap size. The number is stable after forcing GC many times so there must still exist references to these objects somewhere. However, when we use the memory profiler to get a snapshot, it shows only 1 copy of the object in question while our debug UI shows 3 or more copies in the game. The total size of all managed objects in the memory profiler is also much smaller than the used mono heap size we see in the game.

    We are pretty stuck at this point because we can't see what's referencing the leak objects. Is there anything else we can try in order to understand what's leaked/how it's leaked? And why is the object count/size in the player not matching the object count/size in the memory profiler? Does it indicate some kind of memory corruption in our game? If so what can we do to identify it?

    Some more info about the issue:
    * Unity version is 2019.4.19f1. Memory profiler version is 0.2.8-preview.2.
    * We are only able to reproduce the memory issue on Android and iOS. No luck in Windows build nor in the Unity editor.
    * This memory increases issue occurs more often in low-end/low-memory devices. Android users are affected more than iOS users.

    And how we track the object count (we add this as a field to each object we want to track):
    Code (CSharp):
    1. public class ObjectCounter
    2. {
    3.     public static int Count { get { return s_count; }}
    4.  
    5.     private static readonly object s_lock = new object();
    6.     private static int s_count = 0;
    7.  
    8.     private static void UpdateCount(int change)
    9.     {
    10.         lock (s_lock)
    11.         {
    12.             s_count += change;
    13.         }
    14.     }
    15.     public ObjectCounter()
    16.     {
    17.         UpdateCount(1);
    18.     }
    19.  
    20.     ~ObjectCounter()
    21.     {
    22.         UpdateCount(-1);
    23.     }
    24. }
     
  2. MartinTilo

    MartinTilo

    Unity Technologies

    Joined:
    Aug 16, 2017
    Posts:
    2,452
    Hi there :)
    When you search filter the Type column in the All Manager Objects table to check for these numbers, do you search for the type you are monitoring, or for ObjectCounter? ObjectCounter destruction might be delayed to a bit after there are no longer any references to it. After GC.Collect, are you waiting on GC.WaitForPendingFinalizers to make sure it's destructor got called, before you check the count?

    What number specifically are you looking at for this? Profiler.GetMonoUsedSizeLong, Profiler.GetMonoHeapSizeLong or Profiler.usedHeapSizeLong? (I'm assuming the first but your wording wasn't 100% mapping to these so I just want to clarify that part, just in case there is a mix up.)

    At which rate is the count expanding and at which rate the manage memory usage? And how does your managed memory look like in Memory Map (here is a bit of a run-down on how to check for managed fragmentation issues in there)?