Search Unity

Help understanding diff data from Memory Profiler

Discussion in 'Profiler Previews' started by roberto_sc, Apr 8, 2020.

  1. roberto_sc

    roberto_sc

    Joined:
    Dec 13, 2010
    Posts:
    144
    Can someone please help me understand some diff data from the Memory Profiler?

    I have a GameSession class which only a single instance should exist during a session/match. I'm investigating memory leaks so I created a diff between 2 matches.

    I expected to see 1 deleted GameSession instance and 1 new instance, or 1 same and 1 new if memory was leaking, but I have 1 delete and 2 new ones.

    Please take a look at the screenshot GameSessionInstances.png. If the 2 new instances had similar data, I'd assume it's something in my code creating 2 instances, but they are very different: they are both Manage Objects but one of them doesn't have a Native Object Name, Native Instance Id. What does that mean?

    I thought they could be the same instance but they have different addresses and different internal data. The deleted one is also different from the other 2.

    Bonus question: what's the <>c instance? Local variables for a lambda?

    I made these measurements on an iPad Mini 2 but got similar results on the Editor.
     

    Attached Files:

    Last edited: Apr 8, 2020
  2. MartinTilo

    MartinTilo

    Unity Technologies

    Joined:
    Aug 16, 2017
    Posts:
    1,590
    The new one with m_CachedPtr = 0 is only the remaining managed shell object (it otherwise looks to be the same as the deleted) where the native object underneath has already been destroyed but you leaked the managed shell. It still has 11 references, hunt these down to fix your leak.

    .<>c is autogenerated by the compiler. It could be a constructor of the class or yes a lambda.
     
    Last edited: Apr 8, 2020
  3. MartinTilo

    MartinTilo

    Unity Technologies

    Joined:
    Aug 16, 2017
    Posts:
    1,590
    From your other thread, I take it you're on 2019.2. you might want to update to 2019.3 to get cleaner references as that version no longer reports indirect references but only direct ones.
     
    roberto_sc likes this.
  4. roberto_sc

    roberto_sc

    Joined:
    Dec 13, 2010
    Posts:
    144
    Hi @MartinTilo , thanks for helping me - as always :)

    So I assume that all different field values between that one and the deleted one is because they belong to different snapshots, so captured in different moments, is that correct?
     
  5. MartinTilo

    MartinTilo

    Unity Technologies

    Joined:
    Aug 16, 2017
    Posts:
    1,590
    That is correct yes
     
    roberto_sc likes this.
  6. roberto_sc

    roberto_sc

    Joined:
    Dec 13, 2010
    Posts:
    144
    @MartinTilo is there a way to find all empty shell objects that should be dead? Just search for Values that contain "m_CachedPtr=0"?

    If so, sounds like a good idea for a feature: a button that tells me I probably have memory-leaking issues. I can't think of a situation where it's ok to have c++ objects destroyed but empty shell .NET objects living (not considering the ones that are about to get GCed).

    By the way, you mentioned my other post (https://forum.unity.com/threads/memory-profiler-diff-bug.862936/), I didn't understand if I should upgrade to 2019.3 for that because it may be a reference misdirection problem or if you were giving me a general advice. Is it a bug?
     
  7. MartinTilo

    MartinTilo

    Unity Technologies

    Joined:
    Aug 16, 2017
    Posts:
    1,590
    I was thinking much the same. We generally can't tell automatically if you have a leak because there's usually a reason (reference) that's keeping it in memory and without knowing what the current state should be, can't infere if this is intentional or not. Though as long as we make clear that such a view only and specifically shows leaked managed shell objects, it should be possible to list these out.

    (BTW, we we can also only guess at what objects would be about to be GCed by checking if they have a reference path to a valid root.)

    The other thread is a bug, I've answered there too. And the update to 2019.3 would only help with tracking down those 11 references of your leaked shell mentioned in this thread, because they'd be cleaned from indirect references. It's not necessary for either though. Snapshots are taken and analyzed way faster in 2019.3 though too so, I can only recommend going there from a (Memory) Profiler perspective ;)
     
    roberto_sc likes this.
  8. roberto_sc

    roberto_sc

    Joined:
    Dec 13, 2010
    Posts:
    144
    @MartinTilo I want to build a tool that goes through all PackedMemorySnapshot.nativeObjects delegates to search for objects with nativeObjectAddress == 0. This will help me find code that programmers forgot to unsubscribe from events/delegates.

    I can't find a way to get references to/from PackedNativeUnityEngineObject, though, let alone delegates. I saw the "delegates" field in the Name column of an object in the Memory Profiler window, that's what I want. Is there a way to get it?
     
  9. roberto_sc

    roberto_sc

    Joined:
    Dec 13, 2010
    Posts:
    144
    One more thing: still on understanding the diff data, I noticed something on this new screenshot: the empty shell object on this case has a different InstanceID than the one from the old snapshot. This didn't happen in the screenshot on my first post.

    Investigating memory leak due to event subscription, when I took the second snapshot I printed the instance id of the subscribers of the event that this class is subscribing to and I can confirm that the id of the old/deleted object still subscribed to it is -140078 and the new instance is -308824. So why does the empty shell has id 206374? It doesn't matter much, I'm just curious.
     

    Attached Files:

  10. MartinTilo

    MartinTilo

    Unity Technologies

    Joined:
    Aug 16, 2017
    Posts:
    1,590
    uh wait. Native Objects don't have delegates or fields... at least not in a way that would be captured by the memory profiler. The data that the Memory Profiler window shows you is not in the snapshot fields just like that to query it. The Memory Profiler package first crawls the snapshot to generate the data structure for these tables.

    I.e. You would need to go through all PackedMemorySnapshot.gcHandles and treat the target uints as pointers into PackedMemorySnapshot.managedHeapSections.bytes, (obviously taking the sections startAddress into account).
    The first pointer in the bytes points to the TypeDescription.typeInfoAddress which you'd have to search for in the PackedMemorySnapshot.typeDescriptions and then you can use the TypeDescription.fields to look at the FieldDesctription to find fields by name e.g. or use the FieldDescription.typeIndex to look for the type of it in PackedMemorySnapshot.typeDescriptions and see if it is a delegate.

    then you can use the type and field descriptions to find that instance ID and check the value.

    Yes. This snapshot format and API is a pain to work with. We're very well aware. We have to work with this. We're working on both making the format and API nicer as well as improving the filed reader and crawler and exposing all this in a nicer way to everyone but this stuff sadly takes some time and care ;)

    If you're daring, you could also modify the source of the memory profiler package. Not sure if that's gonna get you there faster though. At least if you'd try to make sense of the code for the tables. But you could maybe use the database APIs.
     
    roberto_sc likes this.
  11. MartinTilo

    MartinTilo

    Unity Technologies

    Joined:
    Aug 16, 2017
    Posts:
    1,590
    Are those snapshots from the same session? Instance IDs are not consistent over sessions.

    Also I believe negative instance id's usually refer to assets (and/or native objects) and positive ones to non-assets (and/or managed ones) but I would only use this assumption as an indication, not solid proof of anything. Scratch that, I flipped the sign and mangled the association, @Peter77's post below has it right.
     
    Last edited: May 6, 2020
    roberto_sc likes this.
  12. Peter77

    Peter77

    QA Jesus

    Joined:
    Jun 12, 2013
    Posts:
    6,086
    According to my tests, that's not correct. It should be:
    Code (CSharp):
    1. PackedNativeUnityEngineObject.isPersistent == true // asset
    2. PackedNativeUnityEngineObject.instanceId < 0 // object created at runtime
    3. PackedNativeUnityEngineObject.instanceId >= 0 // object stored in scene
    https://github.com/pschraut/UnityHeapExplorer/blob/master/Editor/Scripts/HeEditorGUI.cs#L351
     
    BigRookGames and roberto_sc like this.
  13. MartinTilo

    MartinTilo

    Unity Technologies

    Joined:
    Aug 16, 2017
    Posts:
    1,590
    Whoops, you're right @Peter77, should've check the source code and double checked the sign thing. What you should not be basing your assumption on was that a negative ID means no association with a file/asset, as that association might happen at runtime and after a negative ID was assigned because the object was created at runtime.

    So, back to
    because the first object which has it's managed shell leaked was loaded into memory as part of the scene, while the others where created at runtime.
     
    Peter77, BigRookGames and roberto_sc like this.
unityunity