Search Unity

Definition of GC.Alloc in Profiler

Discussion in 'Scripting' started by Claytonious, Sep 19, 2018.

  1. Claytonious

    Claytonious

    Joined:
    Feb 16, 2009
    Posts:
    904
    What specifically is the row "GC.Alloc" in Unity's profiler?



    Edit: Yes, I can already make lots of educated guesses - I'm asking for a canonical, *specific* definition. Thanks!
     
    SaSha_K likes this.
  2. GC.Alloc means that during the run time your code (or something in the API) allocates this much of the managed memory.
    This can cause problems later (that's why it has the row in the profiler), because when the Garbage Collector runs, it tends to slow down or even hang your game.
    It is always a good idea to try to write your game allocation free, which means you avoid APIs and techniques which result in allocation on said managed memory.

    It is important that there is no exact science to know how much allocation 'is fine' during a game run time, it depends on many things. Usually (!) the one-time small allocations are fine but heavy allocations or allocation in a loop are very bad and almost surely need to refactor.
     
  3. Claytonious

    Claytonious

    Joined:
    Feb 16, 2009
    Posts:
    904
    You are describing what the "GC Alloc" column means, which can be attributed to any row in the profiler's output. For example, in my screenshot above, we can see 6.5kB of heap allocation happening inside of the Dictionary<> constructor.

    I am asking what the GC.Alloc ROW in the profiler means. Why is there a row, as if it's a method, that has its own entry? When and why does that happen instead of the allocations just being attributed to some existing method (row) as usual?

    Completely understood. I am asking about the GC.Alloc row itself.
     
    John_Leorid and DragonCoder like this.
  4. Sluggy

    Sluggy

    Joined:
    Nov 27, 2012
    Posts:
    989
    That's actually a good question. I'd wager that it's the memory manager allocating more heap space. However, that shouldn't happen unless it already determined there wasn't enough space *after* a garbage collect. And then you'd expect to see that show up in the profiler as well. Perhaps it's a wrapper that abstracts the concept of checking for space, collecting, checking for space again and finally allocating if needed? Out of curiosity, was this a deep profile? If not, maybe you could try that and see how far the GC.Alloc() rabbit hole goes.
     
  5. Ah, got you, sorry about that.

    I believe that's because the hierarchy there is a representation of a call-stack and it's more clear on this way. But I'm not 100% sure in this one, so I may be wrong.
     
    anycolourulike likes this.
  6. Claytonious

    Claytonious

    Joined:
    Feb 16, 2009
    Posts:
    904
    Yes, this was already deep, unfortunately.
     
  7. CapeGuyBen

    CapeGuyBen

    Joined:
    Mar 1, 2014
    Posts:
    7
    I've just been wondering the same thing and worked it out after a little Googling and playing around:

    When deep profiling is not enabled The GC.Alloc row entry is used as a catch-all for allocations performed under the stack level it is at. If you want to see where the allocations actually come from you'll need to enable Deep Profiling which can be done as follows:
    • Editor: If you're profiling the editor you can select 'Deep Profile' and it will recompile and work magically when you next play.
    • Desktop Standalone: If you're profiling a standalone desktop build (macOS, Linux or Windows) you can't control the 'Deep Profile' setting from within the editor (hence it is sort of greyed out in the UI). However, you can enable it by starting up your app from the command line with the argument -deepprofiling. For example, open the build folder for your game in a terminal and enter the following:
      • Windows: MyAwesomeGame.exe -deepprofiling
      • macOS: MyAwesomeGame.app/Contents/MacOS/MyAwesomeGame -deepprofiling
    • Other Platforms Standalone: No idea! See this thread for more discussion on deep profiling on other platforms.
     
  8. MartinTilo

    MartinTilo

    Unity Technologies

    Joined:
    Aug 16, 2017
    Posts:
    2,456
    As I mentioned in the other thread, Deep Profiling to see call stacks for GC.Alloc Samples is not the only way to find out their call stacks. It's the most expensive one though. A better way would be to use the Call Stacks recording feature.


    BTW: From 2019.4 on you CAN controll Deep Profiling from the Editor, if you enabled Deep Profiling Support in the Build Dialog.



    Hierarchy (as opposed to Raw Hierarchy) generally collapses all Samples that share a common sample stack (not a callstack but somewhat similar) into the same entries of the TreeView, so you could have multiple calls to GC.Alloc grouped into one. The Related Data view shows a list of these though in the form of the "N/A" named objects that where allocated in each one (Managed Objects don't have a .name property as UnityEngine.Objects have, which is what would usually be shown in this table) and with call stacks recording enabled, selecting one of these entries gives the call stack for it.

    As to what the Row Item for GC.Alloc means: Like so many other samples (*) that are available outside of Deep Profiling, this is a Sample emitted by an explicitly placed ProfilerMarker in the Unity Native code. In this case, the marker is inserted via a call-back called by Mono / IL2CPP code whenever an object is allocated in the scripting heap that is monitored by the Garbage Collector, or GC Heap for short.


    (* some ProfilerMarkers are automatically placed around Unity Message calls into C# code (e.g. "MyScript.Update() [Invoke]", some are explicitly placed in C#)

    Rows of samples that are NOT emitted by the GC.Alloc marker just sum up the amount of bytes allocated within their child samples. This is so you can sort by that size and more easily track down where along the sample stack the allocations occur. These other Samples don't actually signify that an allocate is made in their immediate scope by themselves.

    Basically, similar to Self Time and Total Time, for the GC.Alloc row, the value in that column is the "Self Allocated" and in all other Rows it is "Total Allocated". Just that in this case it is lumped together into the same column as only this one sample would have its own column otherwise.

    Does that make sense to you?
     
    ilmario and Jack_Truong like this.
  9. sergiusz308

    sergiusz308

    Joined:
    Aug 23, 2016
    Posts:
    235
    @MartinTilo Hi, there, I followed your explanation on GC.Alloc and here's the output from my session:

    upload_2021-9-19_13-36-21.png

    Does this mean that this particular GC.Alloc is emitted by the profiler or my code? If my code, then how to find which method it originated from

    Thanks,
    S.
     
  10. MartinTilo

    MartinTilo

    Unity Technologies

    Joined:
    Aug 16, 2017
    Posts:
    2,456
    Technically speaking both. If ProductionDeviceState is a class you wrote then it's outputted by the Profiler because something your code does within
    ProductionDeviceState.ProcessInProgressFormulas allocates new managed memory, causing the profiler to output that sample because every call that allocates new managed memory is instrumented by this profiler marker.

    Also, you seem to already have turned on Allocation Call Stacks (the sub-sample Profiler.Callstack tells you how much time it cost to collect the call-stack. BTW, the time cost for the GC.Alloc isn't actually measured to reduce instrumentation overhead, so that will always look negligible short when really, it might not be, depending on the amount of allocations and the state of the managed heap at that point). So with that Call Stack Recording option on you can find out more info in what created the allocation by selecting the GC.Alloc sample, opening the Details panel via the drop-down on the right of the hierarchy views toolbar to show "Related (Object) Data" and selecting the "N/A" elements in that list.

    Alternatively or if that doesn't help you narrow it down, and especially when that function is rather long, you can add more detail to the profiler via adding ProfilerMarkers to it to subdivide it into "regions" and then see which of these regions the allocations occur within. That should narrow it down and help find the code that does the 5 allocations.
     
    Trigve and sergiusz308 like this.
  11. jbejar_unity

    jbejar_unity

    Joined:
    Apr 12, 2022
    Posts:
    2
    @MartinTilo - Nothing here helps explain why I'm seeing severals kilobytes of cg.alloc data in an empty unity scene.
    This makes it difficult to find which class is calling cg.alloc every few seconds. We're running into a problem where this cg.alloc is consuming the memory to a point where the application would crash with no errors or logs.

    More or so - I created an empty unity project and ran from empty scene upon startup - I occasionally find 40 bytes of gc.alloc block in the threaded stack. Still can't understand why Unity, under the hood, would continue to produce garbage collection from an empty clean project state.
    Using v2021.3.14f1
     

    Attached Files:

    ilmario likes this.
  12. MartinTilo

    MartinTilo

    Unity Technologies

    Joined:
    Aug 16, 2017
    Posts:
    2,456
    Hi @jbejar_unity,
    I guess the Profiler.Callstack sample is rather confusing but that marker just marks up the time taken to record the callstack. The callstack information itself is attached to the GC.Alloc sample itself, so if you want to see the callstack, you need to select that sample.

    If that also fails, there might be a bug preventing the recording of the callstack that we recently fixed on 2023.2 and are still backporting that prevents callstacks from properly being recorded when using Auto Connect Profiler in the build options. So you might try to turn that off.

    Or you could make a build with Deep Profiling Support and deep profile to see where these GC allows are coming from.

    Btw, I'm assuming you're profiling a build, is that correct?
     
  13. jbejar_unity

    jbejar_unity

    Joined:
    Apr 12, 2022
    Posts:
    2
    The screenshot above was ran from Unity Editor - I compiled the app and found the culprit. Still confused as to why I was unable to find more information about this garbage allocation inside the Editor, when I found the answer from the build instead?
     
  14. MartinTilo

    MartinTilo

    Unity Technologies

    Joined:
    Aug 16, 2017
    Posts:
    2,456
    Hmm i guess that depends on how you were able to find it in the build.

    Generally speaking there can be lots of things happening in the background in the Editor that also just wouldn't happen in a Player, especially on background threads. But it sounds like the same thread work was also happening in the Player?