Search Unity

Question Collect all the Garbage

Discussion in 'Scripting' started by IronHelmet, Jan 8, 2024.

  1. IronHelmet

    IronHelmet

    Joined:
    May 2, 2017
    Posts:
    85
    I'm attempting to find some memory leaks in our game, and I want to force a garbage collection to make sure there is no references to garbage when I take a memory sample with the profiler.

    We are using the Incremental Garbage collector. There are some load screens which is a good place for us to pause and do a full, slow garbage collection. (even in release)

    Should I simply call collectIncremental in a loop until it returns false?

    Is there a better method to collect all the garbage synchronously?

    Also is System.GC.Collect() still supported when using the incremental garbage collector?
     
    Last edited: Jan 8, 2024
    Yuchen_Chang likes this.
  2. CodeSmile

    CodeSmile

    Joined:
    Apr 10, 2014
    Posts:
    5,953
    For debugging memory leaks I would recommend to disable incremental GC for the duration of the debug session so that you can rule out any side effects or unexpected (possibly non-deterministic) behaviour of the incremental GC. I'm thinking this will make finding those leaks and GC hotspots easier. Even disabling GC altogether may be a viable debugging approach as you can see over time how memory accumulates in the (memory) profiler.

    The incremental GC really only helps in avoiding those spikes by flattening them out over time so you have a better chance of still getting steady 60 fps vs a several hundred ms spike in a single frame.

    Calling GC collect in a loop sounds like a bad idea. It may never fully collect and you'd end up in an infinite loop.

    The manual may have some additional tips:
    https://docs.unity3d.com/Manual/performance-garbage-collector.html
     
    CodeRonnie and Yuchen_Chang like this.
  3. MartinTilo

    MartinTilo

    Unity Technologies

    Joined:
    Aug 16, 2017
    Posts:
    2,454
    If it's referenced, it is, per definition, not garbage. If it is no longer referenced, the Memory Profiler package won't find the Object.

    Further, to the Memory Profiler, an unreferenced managed Object is virtually the same as a collected one: empty heap space. The tool is currently not getting the kind of detailed information from the GC that it would need to be able to distinguish these (aka Free Blocks List and Free Small Blocks List).

    What a GC.Collect may do though, if seemingly a bit at random (aka whenever GCCollectCountSinceStartup % 6 == 0), is to unload some unused pages, reducing the unused Reserved amount. Beyond getting "cleaner" total values for Memory profiling, I do not recommend doing that. I've gone into more details around that here. The only thing peftleft to add to that is: if you add a for loop to call GC Collect 6 times, check Profiler.GetMonoHeapSizeLong before the loop and after every GC.Collect + GC.WaitForPendingFinalizers combo. If that value went down break out of the loop, you want be doing any better.

    If you meant to say: Collect unused Asset Memory via Resources.UnloadUnusedAssets, (because these objects would actually show up in the Memory Profiler even if they are unused and might keep more memory around, including managed memory) then maybe read my posts over here as well. Because you might want to inspect these in the Memory Profiler (instead of expunging them before taking a snapshot) and ensure you clean them up properly by calling Destroy on them.
     
  4. IronHelmet

    IronHelmet

    Joined:
    May 2, 2017
    Posts:
    85
    Thanks for the response Martin, I was not sure how the garbage collector worked so thanks for clarifying.
     
    MartinTilo likes this.
  5. MartinTilo

    MartinTilo

    Unity Technologies

    Joined:
    Aug 16, 2017
    Posts:
    2,454
    No worries, And yes, just to clarify:
    Yes, that can still be called. in fact, if you thrash the GC hard enough, it might fall back on it if the iterative approach never gets to finish before it's work is utterly invalidated by the GC thrashing.