Memory Management and List

Hey all,

Inspired by this question on textures and their memory usage, I decided to spend a little time playing around with Unity and its memory usage, and I found several things that go against my understanding of how this is supposed to work, and frankly, I’m a little disturbed, and hoping someone can help me shed some light on what’s going on. O_O

First off, the project is an empty testscene. The only GameObject present is a camera with a script attached. The script looks like this:

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class NewBehaviourScript : MonoBehaviour {

    List<int> test = new List<int>();

    void OnGUI()
    {
        if (GUILayout.Button("Add items"))
        {
            for (int i = 0; i < 100000; i++)
            {
                test.Add(i);
            }
            Debug.Log(test.Capacity);
        }
        if (GUILayout.Button("Delete items"))
        {
            //test = new List<int>();
            test.Clear();
            test.TrimExcess();
            System.GC.Collect();
            System.GC.WaitForPendingFinalizers();
            Debug.Log(test.Capacity);
        }
    }
}

As you can see, the only thing the script does declare an empty List, then paint two buttons, one that lets me add 100.000 items and another that deletes and trims the list. I have two questions, both of which are memory related:

  1. When I run this scene in a freshly opened editor (Unity Pro 3.4), the total memory usage reported by the profiler stabilizes at 196.5MB (or so). When I stop, and then immediately click Play again, the reported total memory is 197.6. When I stop, then hit play again, it’s 199.8. Then 201.2. Then 202.5. I can keep doing this as many times as I want. Why does the editor leak ~1MB memory every time I run a basic program in it? Is this because some left-over data from previous executions in the editor doesn’t get garbage collected till much later, or something?

  2. About the list that gets manipulated by the buttons… I can cause the expected jumps in memory usage every time the runtime system doubles the List’s capacity to make room for more items. That matches my expectations just fine. But no matter what I do, I can’t seem to cause the memory to be freed again. According to MSDN, calling Clear and TrimExcess is supposed to first wipe the list, then set the capacity to the number of items (0, after clear), which reallocates the memory. I.e. if the capacity gets set to 0, it should free the memory previously allocated to List items, right? But it doesn’t. I’m not seeing a drop in total memory in the profiler, and I’m not seeing a drop in Windows’ Task Manager if I run a build outside the editor, either. Why doesn’t TrimExcess free my memory? It makes no difference if I re-instantiate the entire list with the new-keyword. I’m still not getting my memory back. This doesn’t make sense to me. :-/

UPDATE:
I just tested this in a standard WinForm app with the same two buttons, using Microsoft’s compiler and .Net runtime (MS Visual C#). The memory management is correct there. Clear+TrimExcess definitely wipes the list and frees the memory there. This behavior is something Unity/Mono.Net related.

Christian, I was glad to see someone else was having the same issue as I. Here is what I did (with help) to resolve (I believe) the issue.

public void DeleteAllCubes()
{

    // At this stage, a grid of 400 cubes has been created via code and each one added to the list “allFloorCubes” as GameObjects
    // allFloorCubes currently has a Count of 400 and a Capacity of 512. I think the 512 is memory allocation.
    
 // I want to delete all my prefab cubes on screen…
    foreach (GameObject cube in allFloorCubes)
        Destroy(cube);
    
    // at this point, I only Destroyed the GameObjects so the grid is gone, I have not touched the list “allFloorCubes”.
    // allFloorCubes still has a Count of 400 and a Capacity of 512. I think the 512 is memory allocation.

    // now I can do a Clear against the list.
    allFloorCubes.Clear();

    // allFloorCubes now has a Count of 0 BUT the Capacity is STILL 512 
    // so it’s like Unity has not released the memory resources for the list, If I rebuild another list the Capacity would double to 1024, ect ect.

    allFloorCubes.TrimExcess();

    // allFloorCubes now has a Count of 0 AND a Capacity of 0
    // the memory allocation has been cleared and there was much rejoicing.
}

// for redundancy or just another way to clear the list and it’s memory allocation…
public void InitializeList()
{

    try
    { allFloorCubes = null;}
    catch { Debug.Log("Did not deallocate list 'allFloorCubes'."); }

    try
    { allFloorCubes = new List<GameObject>(); }
    catch { Debug.Log("Did not create list 'allFloorCubes'."); }

}

Would it be possible for you to use another List class?

Define this at the top:

using List = System.Collections.Generic.List<int>;

Then in your code would declare it like this:

private List test = new List();

Does this still provide a memory problem?

Try adding Debug.Log(System.GC.GetTotalMemory(false)); after you fill the list and again after the clear. This should show you the number of bytes .net thinks are available after each operation.

I’m a little weary of trusting Task Manager as your code is not the only thing going on in that process. So at least this way you can peak in at what the memory manager thinks it has at hand and see the effect of your code on it.

I’m at work or I would test it out myself. BTW: if you change the parm to ‘true’ it will force a collection prior to reporting size.

Also consider trying it outside of OnGUI, maybe with a couple planes, GUITextures, whatever, to take the GUI system out of the equation.

In regards to your second point, I’m unsure of the behaviour in the editor but on iOS and Android devices any memory that Unity acquires is not released back to the OS even though the memory is freed within Unity.

For example, an app is using 50mb of memory. Then, for whatever reason, it temporarily needs 60mb but then releases those extra 10mb. The memory the Unity app is using stays at 60mb but within that 60mb there is 10mb free to use. Only if the app goes over the 60mb will the memory being used increase, and stay at that level.

Effectively once Unity uses memory it is never released back to the OS until the app exits, but the memory is free within Unity for use later on.

At least, as far as I am aware.

Pretty low level and worrying issue. Have you checked if the memory clears correctly after program exit? If the program hogs memory and doesn’t release those “1mb” after progam exit, it’s definitely a garbage collector bug.

i’m having the same problem, but its too terrible in my case. The profiler memory & the task manager memory both increases and the funny thing is i dont have anything in the scene, except the camera which has no script or anything. I deleted all the assets and cleaned my project explorer window to make sure my game has nothing but the camera. Still, 1MB everytime I run it. So, weird!!! Anyone has a solution?