Clearing memory is extremely difficult

For weeks now I have been working on memory issues, I can see that the memory increases between scenes, this causes issues on consoles, I tried everything I can to make it go down, an empty scene loaded after a normal scene doubles it’s size for absolutely no reason, I checked for everything I could find online, (like static elements keeping it alive for example), I’m comparing everything in the memory profiler and see no reason for any of this to exist in a totally empty scene, this is the code I’m using to try to find the cause, you can see that I’m unloading everything many times and it is proving to be totally useless, I also tried to Unload the scene (and many things more that I tried over the days):

public class RedirectToSceneIndexTest : MonoBehaviour
{
    public int sceneIndex;

    // Start is called before the first frame update
    void Start()
    {
        Resources.UnloadUnusedAssets();
        StartCoroutine(LoadSceneAfter());
    }

    IEnumerator LoadSceneAfter()
    {
        Resources.UnloadUnusedAssets();
        //SceneManager.UnloadSceneAsync(SceneManager.GetActiveScene().buildIndex);
        yield return new WaitForSecondsRealtime(12);
        SceneManager.LoadScene(sceneIndex);
        Resources.UnloadUnusedAssets();
        yield return new WaitForSecondsRealtime(1);
        Resources.UnloadUnusedAssets();
    }
}

with this code I first load an empty scene that has nothing but a camera, the scene is 466 mb, a normal game scene is loaded, then the empty scene again, now that scene is 1gb+, is there a way to clear the memory and make the empty scene go back to it’s original size?

1 Like

try this Unity - Scripting API: Scripting.GarbageCollector.GCMode
also call GC.Collect(); like 3 times in a row , should help , Resources.UnloadUnusedAssets() doesnt work to well , you could also try Unity - Scripting API: Resources.UnloadAsset see if that works for you
unitys garbage collection system is quite slow , Not really built for fast unloading so you have to nudge it quite often , when auto saving or loading a new save try do a GC.Collect(); , also might want to try System.GC.Collect(0); do it multiple to times in a row , to force a collection

1 Like

Thanks for answering, I tried all that, but still same problem, and the references screen is very useless, for example I have textures loading in a empty scene that comes after a normal, when I check the reference I see this:

8055959--1040180--upload_2022-4-18_0-32-17.png

How? there is no materials anywhere in this scene, this is a material indeed being used in the previous scene but how does it keep going from scene to scene?

In some other elements the reference screen is infinite, it basically is pointing to everything related to that asset but not to the root of what is keeping it there.

I’m spending so many hours on this and I don’t seem to be anywhere near to finding the issue

not very familiar with the memory profiler , it seems more then likely that somewhere unity is storing all references in some kind of static list or static array . try making another scene thats empty , then slowly add things back in and switch scenes till the problem shows up , otherwise if even in an empty scene with say a terrain object in it still keeps the reference to it then there is a bug either with unity itself or with an plugin.

try these and see , also try on a new project switching between scenes , if both of these tests result in the same issue then its unity’s fault .

Yeah that does absolutely nothing differently in Unity. The GC is non generational so the passed 0 does nothing. Calling it multiple times will only return empty (and therefor virtual) memory pages to the OS on every 6th GC, which means that GC Reserved will seem to go down but as that memory is virtual, it doesn’t actually change anything but increase your managed memory Fragmentation. Just don’t do it. Calling it once should be more than enough.

Resources.UnloadUnusedAssets is called automatically before a destructive scene unload, i.e. while that material is still in use. The logic is that it’s likely to be used again in the next scene and constantly loading and unloading would be expensive and pointless. But if you call it again after the scene is unloaded, it should remove that material.

In that screenshot, if you select the Material, either the native or the managed object, the selection details section below the references would tell you if that’s actually the case in the Status field and the Help section.

Right now it shows all references and is factually correct but it can show more than would be helpful. We’ll tidy this up in a future version but part of that cleanup relies on new information we added to the memory snapshot in the engine’s backend that so far only exists in 2022.2.

Basically, the first time you hit a GameObject in the References, you can stop digging deeper in most instances. In 2022.2 we’d be able to tell you which scene and where in the hierarchy that GO is but in older versions we can’t.

Also, whenever there is a switch from Native Objects to Managed Objects in the references chain, there are some conditions that would mean that this connection is essentially non-owning, or less binding. E.g. a managed object referencing a native scene object (of type GameObject or Component) the managed object can’t hold that object in memory for longer than the scene exists. Also Native Objects of any type can be removed from Memory with a call to Destroy () them, even if managed objects reference them. And if the only managed object referencing them is, like in this example, their managed Shell object, the asset can be unloaded.

As mentioned, the Status and Help fields already take most of these scenarios into account and should provide a good first indicator of what you could do about these objects.

Thanks for Answering Martin, I have been reading you advices of the last 3 years on my search to fix this issue so I’m aware that you have been repeating this around a lot and appreciate the effort, it’s not me being lazy, I’m just desperate at this point.

The problem is that my game has been on development for the last 5 years by multiple developers, so the code base is massive, it is pretty well organised, we are all experienced developers so we made sure of that, but finding exactly what is causing this is a big challenge, and Unity didn’t at any point alerted us about any memory issues, which seen how bad it is, should have happened at some point, because we have been using static for years now.

I’m trying to figure out now at what point static variables mess up everything, would this cause a memory issue:

8056952--1040444--upload_2022-4-18_13-22-51.png

now in another class I use it as such:

List<GameObject> respawnTriggers = SearchService.SearchRespawnTriggersInRadius(respawnTags, transform.position, VisionRadius);

Is respawnTriggers now holding memory forever?

Doesn’t seem to be the case, I even left an Update with this piece of code to spawn the hell out of UnloadUnusedAssets, it still shows dozens of textures in an empty scene and increases it’s memory greatly:

    private void Update()
    {
        Resources.UnloadUnusedAssets();
        GC.Collect();
    }

Yes, but unless I’m missing something it does not tell me where it’s happening, it says “Static” on notes, but I went across the code taking care of static variables and making sure they are null and that those objects are destroyed before moving to the next scene, but I’m obviously missing something and Unity doesn’t seem to point me to what is causing the issue, nor gives me any warning in the code itself, so I’m spending hours going across classes trying to figure out what may be the issue

8056952--1040453--upload_2022-4-18_13-32-7.png

I wish that unity had a “Resources.PleaseDoGetRidOfEverythingOfThePreviousSceneForReal()”;

1 Like

No worries, I’m glad that my old posts still help but my ambition with the Memory Profiler as a tool and with the accompanying documentation (both very much still in progress) is that such forum trawling will eventually not be necessary anymore. The Selected Item Detail’s Help section is a first step towards that.

The function itself? No, but if you’re keeping the retuned list of GOs in a static field somewhere, or in a MonoBehaviour on a GO that is marked DontDestroyOnLoad (or just in a different scene that you won’t unload) you’ll be leaking the managed Shells for these GOs if you leave the scene they are in (their native objects, which are way bigger in size in memory, would be destroyed on unloading that scene, so that’d be fine). So even if your respawnTriggers are static, the worst that will happen ist that you’ll leak GO Manager Shells at about 24 B or so each. They might cause some managed Fragmentation, i.e. keeping otherwise empty managed space around, but other than that, should be fine.

Static reference chains to Assets (i.e. non- Scene Object type native objects, like textures or materials) are more detrimental as they will keep the native Asset Memory around even if nothing else uses them anymore.

That’s the Managed fields of the Texture2D class and not within your code but within Unity code. It’s not holding anything in statically rooted memory except for these 2 ints. What else is holding a reference to that texture though, i.e. if you unfold the references to it? And when you select these things holding the reference to that, what do the selection details say about these objects?

If you’re already calling the Asset GC via Resources.UnloadUnusedAsset() (which btw already also triggers a GC.Collect) then everything that’s still in memory after that point, (plus maybe some frames, keeping in mind that the scripting GC may be incremental and the Asset GC could operate multithreadded and async) is going to be memory that is somehow still in use.

Figuring that out and the why of it is currently a bit if a manual workflow but I’m aiming to get a view into the tool that will attribute the memory impact to the anchors/roots that keep that memory around, to make finding the main culprits easier. That’s still a few versions out but not too far off I hope. Even though that doesn’t help you yet, I thought I’d mention that it’s on our radar.

1 Like

good to know!

As crazy as it may sound, you might want to try deleting all Resources.UnloadUnusedAssets();

I was starting to freak out as loads between scenes started to get really long, as well as regenerating meshes.

See this short little thread here: it took me nine months to figure out how bad an idea Resources.UnloadUnusedAssets(); is. I don’t think it does exactly what you think it does.

https://discussions.unity.com/t/843371/3

IF you look at the end of my last post in the above thread, you see this link, which saved me. I was starting to have an anxiety attack not being able to figure out what was going on:

https://discussions.unity.com/t/589376/5

Yeah, you definitely don’t want to call Resources.UnloadUnusedAssets every frame but if you need to get your memory pressure down after a scene is unloaded, calling it once should not be detrimental. As I mentioned, it triggers the scripting GC (so that it can know how many managed references to assets still exist) and generally takes a moment to run, depending on how many native objects there are in memory.

Generally speaking, a system where you know what you need to unload, e.g. because everything is organized in AssetBundles, possibly using Addressables, would be more ideal.

Also, the Scripting GC is triggered automatically when new Scripting memory is needed. The Asset GC is not triggered automatically, except for before destructive scene unloads and in the Editor, so if you are slowly leaking Dynamically Runtime Created Asset objects, you’ll eventually crash out of memory. Triggering the Asset GC regularly can prevent that crash but is obviously just a bad stop-gap solution. Such leaks need to be plugged with controlled Destroy() and Unload calls.

Long way to say: I wouldn’t advice to remove all Resources.UnloadUnusedAssets(); calls but certainly to make sure they happen in a centrally controlled fashion.

this is interesting , so if I call destroy on an object , unity will still keep the memory for it incase its used again ? but calling Resource.UnloadUnusedAssets(); will clear this memory , hope I’m reading that right , would’ve been great to know , had a memory related issue with my voxel terrain , anyway good to know!

No, what I mentioned only applies for Asset type objects that were loaded/created and still used before a level was destructively unloaded (i.e. not after an async or addive load).

(Also, Scene Objects that are not marked DontDestroyOnLoad will always be Destroyed when their level gets unloaded, managed shells referenced from outside of that scene may survive as fake null though)

Calling Destroy or Resources.UnloadAsset on a particular instance will get rid of that instances memory. It’s managed shell might stick around though and become one of those infamous Fake-Null objects, aka a Leaked Managed Shell, unless all references to it are getting set to null or the objects these references are on get Garbage Collected.

1 Like

I ended up finding the last point that causes some of the issues, it was in a third party tool with a static variable (and a very popular one too with thousands of downloads, so it took me by surprise, I told the author about it and apparently he was not aware that Unity keeps memory on statics variables forever), I still have some memory leak but they are minimal and I have 0 clue of where they come from, so I will see if I can get away with ignoring them, I really wish that this were something that could be addressed in an easier way, or at least we would receive some sort of warning so we don’t find out about it at such a late stage and the warning would also help us find those vulnerable cases, I can see that is a constant headache for everyone.

I really do appreciate all the help and the effort you put @MartinTilo !

1 Like

I can see that I’m getting a lot of memory out of “Reserved”, how do I get rid of that? this is the same scene:

The first time:
8093429--1047338--upload_2022-5-1_18-10-21.png

a lot of “Reserved” when opened from another scene:
8093429--1047335--upload_2022-5-1_18-9-59.png

That usually means that you hit a high water mark and have since reduced the memory usage, leave the reserverd bugger space behind. It can e g. happen when you have scenes which each load a big chunk of asset memory into memory with not a whole lot of overlap between them and no empty scene load in-between that would help unload the assets between the scenes. Then the second scene loads in it’s assets before the first scene’s assets are unloaded.

You can use the MemoryProfiler.TakeSnapshot API to take a capture just before the scene change, and just after it. If you load an empty scene in between, you can also capture the memory there after it is loaded and again after Resources.UnloadUnusedAssets is called (though that call should not be needed if you don’t memory Profile and just load the next scene from the empty one).

Then you can compare these in compare mode and e.g. see if there is a bigger gap in the Fragmentation page and what was there before. (Though that page is currently relatively cryptic to most people and needs revising, it is currently the best tool to hint at 'empty space memory usage")

An empty scene in between seems to make no difference unfortunately, both empty scenes get a lot of reserved memory when loaded in a row

Well, the empty scenes would have empty reserved memory usage as the memory that was reserved & used in the previous scene likely wasn’t returned to the OS, it just isn’t used anymore because the scene is now empty. This technique is there to avoid peaking beyond what is needed during the load. If loading the empty scene would increase the reserved space compared to what was reserved in the previous scene, that’d be odd.

I have made the project for hand tracking using opencvsharp and related package.
Well, I am very upsetting due to memory leak.
I reviewed all code and project setting but I can not find the issue and can not figure out the solution.
So I created empty scene which is independent from other scene and execute it, Very strangely at that time, memory leak happened, too.

1 Like