Search Unity

  1. Unity Asset Manager is now available in public beta. Try it out now and join the conversation here in the forums.
    Dismiss Notice

MemoryProfiler: Improvement for finding memory leaks

Discussion in 'Profiler Previews' started by owenlshinyshoe, Jun 2, 2021.

  1. owenlshinyshoe

    owenlshinyshoe

    Joined:
    Aug 4, 2017
    Posts:
    8
    I made a modification to Memory Profiler to make it easier to find memory leaks. Here's the code in case it's helpful to anyone.

    When I'm looking for memory leaks, what works best for me is to take a snapshot in the main menu, play the game a bit, go back to the main menu, take a snapshot, and repeat that process, each time playing the game in exactly the same way, until I have 7 snapshots. Then I open the last one (snapshot 7) and look at which allocations were new in snapshots 3-6 and yet still in snapshot 7. These are the leaks.

    The allocs in snapshot 7 that were new in snapshot 1 are the game's global managers, which are fine. The allocs that were new in snapshot 2 (the first gameplay) are probably legitimate caching. The allocs that are new in snapshot 7 include legitimate things like the main menu. But the allocs that were new in snapshots 3-6 should have been deleted. If there's a leak, even a small one, I'll see allocs that are from snapshot 3, and probably see similar ones from snapshot 4, 5, and 6, since probably the same leak happens every time I play.

    Unity's Memory Profiler (I'm using 0.2.9-preview.3) comes very close to providing this with the Diff feature. So I only had to write a bit of code to extend it to get the functionality I needed. Thank you, Unity, for allowing us to see and modify the source.

    I've included my modifications here in case anyone else is interested. My installation procedure is crude:

    If you have Memory Profiler 0.2.9-preview.3 installed:
    Unzip LeakAnalysisModification-MemoryProfiler-0.2.9-preview.3.zip on top of your Library\PackageCache\com.unity.memoryprofiler@0.2.9-preview.3 folder.

    If you have a different version of Memory Profiler installed:
    Unzip LeakAnalysisModification-MemoryProfiler-0.2.9-preview.3.zip and Base-MemoryProfiler-0.2.9-preview.3.zip into a temp folder and compare them to see the changes I made. Then apply the changes by hand to your Library\PackageCache\com.unity.memoryprofiler@x.x.x code.

    How to use it:

    1. In the Editor, open Window/Analysis/Memory Profiler.
    2. Delete your existing snapshots.
    3. Start your game
    4. In your game, go to the main menu or somewhere constant where you want to take your snapshots. I'll call it the main menu.
    5. In the Memory Profiler take a snapshot. Rename it "Snapshot-1" or something that will indicate which one it is in the sequence of snapshots.
    6. Play the game a bit in a way that you can repeat later. I play two battles with the random seed forced to 0 so it plays the same every time.
    7. Return to the main menu.
    8. Repeat steps #5-7 until you have at least 5 snapshots, ideally 7-12 snapshots.
    9. Choose Leak Analysis/Generate.
    10. Wait a few minutes while it opens each snapshot and gets data out of it. It's done when it logs "Generate Leak Data done" in the Editor console. It created the file LeakData.json in your snapshots directory.
    11. Open the last snapshot and go to the Table/AllObjects view.
    12. Find the "Snapshot" column (at the far right), right-click on it, and choose "Group".
    13. Look in any of the groupings that aren't the first two snapshots or the last one. These are the potential memory leaks.

    MemoryProfilerLeakMod.png
     

    Attached Files:

    MartinTilo and alexeyzakharov like this.
  2. alexeyzakharov

    alexeyzakharov

    Joined:
    Jul 2, 2014
    Posts:
    507
    Thank you for sharing!
    This is really great demonstration of leaks detection workflow. Happy to see snapshot having data needed to do the analysis and generate the report!
     
  3. MartinTilo

    MartinTilo

    Unity Technologies

    Joined:
    Aug 16, 2017
    Posts:
    2,453
    Thanks for sharing this idea. Specifically I think we'll likely look at 3 snapshot diffs as a future feature for this, which seems to be the crutial bit here (snapshot 1 as baseline, snapshot 2 with new allocations made, snapshot 3 which should be back to baseline).
    However, we need to adjust a bit of how diffs work (and the codebase already shifted quite a bit internally too) right now so taking over this code just like that won't be possible. Definitely an inspiration though. Hopefully we'll be able to provide better API hooks and export options to allow building such extensions more easily also to allow integrating them into a CI/CD setup.
     
  4. owenlshinyshoe

    owenlshinyshoe

    Joined:
    Aug 4, 2017
    Posts:
    8
    Here's a version that works with Memory Profiler 0.7.1-preview.1. See above for how to use it.


    Let me make another pitch for why you would want to use this to find memory leaks, because I may not have been clear in my previous description.

    The way to find memory leaks in a game is to record all the memory allocations while you play, dump a snapshot of the current allocations, and then identify which allocations are leaks. The leaks are the allocations that should have been freed but haven't been.

    How do you know which ones should have been freed? You add a time number to each allocation entry that you record. In the game you increment that time value when you enter a new section of the game. So let's say you increment the time value at the beginning of every level. Then you play 10 levels of the game and take a snapshot. The snapshot is a list of memory allocation entries, each entry with a time. The time values range from 0 to 10 because you played to level 10. Allocations with time value 0 were allocated before you entered level 1. Allocations with time value 4 were allocated while you were in level 4, and so on.

    You expect that there shouldn't be any allocations with time values 2, 3, 4, 5, 6, 7, 8, 9 because memory allocated in those levels should have been freed now that you're in level 10. Let's say you see that there are about 20 EnemyUnit class allocation entries with time value 4. And another set of 20 entries with time values 2, 3, 4, 5, 6, 7, 8, and 9. That's probably a leak, because it seems like 20 EnemyUnit instances accumulate every time you play a level. You then look in the code to verify that all the EnemyUnits are supposed to be freed between levels. Then you write code to free EnemyUnit at the end of a level. And then play the game again to verify that these entries no longer show up in the snapshot. In this way you can squash all the memory leaks.

    Now back to the Unity Memory Profiler. You don't have the ability to add a time number to the tracking of each memory allocation. But you can get the same result by taking a snapshot in the same place you would have incremented the time value. So you'd play 10 levels, taking a snapshot at the beginning of each level. Then, for the last snapshot, you have a list of memory allocations and by scanning the previous snapshots you can figure out when each allocation entry was made. So the time value corresponds to which snapshot (1-10) that the allocation first appeared in.

    That's what this modification does. It scans all the snapshots you've taken in chronological order. Then you look at the last snapshot and it populates the "Snapshot" column with which snapshot the allocation first appeared in, i.e. its time value.