We've started work on a new memory profiler

From: https://bitbucket.org/Unity-Technologies/memoryprofiler

MemoryProfiler

Unity 5.3a4 has a new very lowlevel memory profiler API. It can tell you which objects got blamed for how much c++ memory allocations. On IL2CPP platforms, it will also give you a dump of the entire c# heap, as well as c# type descriptions.

This API is too low level for most people to benefit from. The intention is to write a much nicer UI window ontop of this API that will actually be readily useful for many users, in helping them figure out which objects are loaded, which objects take a lot of memory, and most important, why that object is in memory. This repository is that nicer UI window, very much in progress of being built.

A common pattern of ā€œmemory leaksā€ that we see, is if you have a c# class with a static field, that contains a list, that has an object, which has a reference to a big Texture2D. This Texture2D will never be unloaded, because it is still reachable from C#. Figuring out this is the case is not very straightforward. This new memory profiler window will make it straight forward, and if you select the big Texture2D, it will show you a backtrace of references, all the way to the static c# field, (including the classname) that is causing this Texture2D to be loaded. (this only works on il2cpp platforms, as we need the whole il2cpp heap to do this backtracking)

We have taken the, for us new, approach of shipping the low level API in the Unity product itself, and already opensourcing this memory profiler window long before it is ā€œready enough to be included in unity properā€. Weā€™ll develop this window as an opensource project. Contributions are welcomed. At some point weā€™ll feel that the feature is ā€œgood enoughā€, at which point weā€™ll most likely ship it out of the box with Unity.

Today, the UI part of this feature is still very rough. We decided to opensource it anyway, as when your project is in a state of ā€œwe need to use less memory, what do we do nowā€, you probably care alot more about finding your memory leaks today, then finding them 6 months from now with nicer buttons and user experience.

Usage:

  • Make sure youā€™re running Unity5.3a4 or later.

  • Build an il2cpp project & run it (any il2cpp platform should be fine).

  • Open the user project in this repository.

  • Open the normal profiler window.

  • Connect it to the player as you would normally profile.

  • Open the memory profiler window (Window->MemoryProfilerWindow)

  • Click ā€œSnapShotā€.

  • or watch me do it in this video:
    https://www.youtube.com/watch?v=7B6xRYMzst8

16 Likes

Oh snap. I actually googled recently looking for some kind of memory profiler. This will be great.

Is there going to be a snapshot diffing option ?

Always great to have some low level APIā€™s! Any details on that API?

Very exciting! I hope we see this become stable & il2cpp becoming active for more platforms so we can also use it for android etc.

Any hope of this making it back into 4.6?

A lot of mobile developers are still in 4.x, with pretty good reasons for staying away from 5.x for now.

And the current profiler gives some unexpected/inconsistent stats when trying to investigate memory use on iOS, especially when compared with the XCode memory use figures.

What are the mobile issues in 5.x preventing your migration? that would be the most important thing for Unity afaik.

yeah, I documented them in the docs. itā€™s not for the faint of heart though :slight_smile:

L

Weā€™ll probably make it work for mono as well. just need to find the time somewhere, and I didnā€™t want to hold up this initial ā€œreleaseā€ for it.

1 Like

I believe you can already use IL2CPP for Android in 5.2. At least in the betas you could, havenā€™t yet switched to 5.2.

The loss of Beast, really.

Even if the new system was completely bug free (itā€™s not - lightprobes have been broken for months - apparently fixed now, but the fix hasnā€™t shipped yet), thereā€™s also the issue of having to relight and rebake a fairly large and lightmap-heavy game to make it work with the new system - and thereā€™s still a question of how lighting quality (and overall game performance) would compare with 4.x.

Hi Lucas, thanks for the write-up and making this tool available, even in an early state. You mentioned that itā€™s available in Unity5.3a4. Does that mean itā€™s also available in 5.3.0b1? If not, where can we find a4?

a4 was the first build that had it available. b1 is newer than that, so it has it too.

The new memory profile is revealing some interesting things.

We make use a of a few singletons in our game - singletons that manage some parts of our UI (NGUI). For example, a ScoreboardUI script/singleton that exists in some game scene. In initial menu scene, the game UI atlas has not yet been loaded. When I load the game scene, the game atlas gets loaded, the ScoreboardUI script sets the singleton reference to it self, and the ScoreboardUI script updates some UI Labels to show the scores for the game. When I leave the game scene to go back to the menu scene, everything is destroyed, but the Memory Profiler reveals that the game UI Atlas has not been unloaded because the ScoreboardUIā€™s static Instance field is referencing it (Indirectly - the Scoreboard UI references some UILabels which in turn reference the atlas but the shortest path to root is ScoreboardUI). The strange thing is, none of these objects exists anymore. Everything is already destroyed. The gameobject with the ScoreboardUI script is destroyed and the entire UI was destroyed too.

Turns out I have to explicitly set the static ScoreboardUI.instance value to ā€˜nullā€™ in the ScoreboardUIā€™s OnDestroy method. When I do this, Unity releases the atlas correctly.

Hey Proddiga,

I am very happy to read your post, as it is exactly scenarios like this that motivated us to make the memory profiler. As you can see, object lifetime management, across c++ and c# is non trivial, and can catch by surprise the most experienced developer. (both in the user community at large, but also developers working at unity). So Iā€™m very pleased that the profiler was able to tell you what was going on, and made you realize how to fix it.

Aside from that, a few words on the actual scenario of having to set the static variable to null here. What is going on, is that c++ objectsā€™ lifetime is managed explicitely (you can destroy a c++ object with Destroy(), or with loading a new scene). but c# object lifetime management is managed by the c# garbage collector, and it will only be destroyed once nothing references it anymore.

ā€œMonoBehaviourā€ is a funky component, in that it is part c++, but also part c#. so when you Destroy() a UI element (UI elements are implemented as MonoBehaviours), the c++ side is dead, but the c# side, including its fields that can reference other things, stays alive until nothing else references it.

ā€œCan we make this situation better?ā€ is an interesting question. one thing we can do (in additional to providing additional diagnostics like the memory profiler does), is that if you Destroy() a monobehaviour component, we could choose to clear all the monobehaviours c# fields. (and fields in structs in fields). Iā€™m reluctant to go there, mostly because of backward compatibility. today you can Destroy() a monoebehaviour, but continue to access its fields. I think that if we were to design things from scratch, I would indeed clear all fields on Destroy(). On that note, we area actively researching adding a ā€œmore modernā€ side-by-side alternative to MonoBehaviour, where we basically get to correct design ā€œmistakesā€ (not 100% sure yet this one is a mistake, have to give it a bit more thought). Iā€™ll put it on the design agenda for this new class to discuss with the team if we should clear fields on Destroy().

Bye, Lucas

1 Like

Thanks for the reply Lucas. Weā€™re trying to solve one of these issues right now. The memory profile just says one of our objects (an object that is DontDestroyOnLoad, not a singleton) is referencing the atlas material from the previous scene (And, in turn, that material is referencing a huge atlas). The strange thing is, this script has no material references (even though the memory profiler says it is referencing the material directly). I have no choice but to go through the script and just explicitly set anything that could be causing an issue to null when the scene changes.

At least with this new memory profile I can build the game and take a memory sample to check if I fixed the problem.

This is all making me realize just how, uhh, ā€˜dangerousā€™ DontDestroyOnLoad and static fields can be if you are not super careful.

Prodigga, Can you post a screenshot of the profiler where it says the material is referenced directly, and post the actual script in question that has no direct material references?

@bluescrn : we have no plans to bring this feature back to 4.x. in fact, weā€™ll continue to do 4.6 patch releases until december of this year. read about the plans for different versions here:

@Lucas-Meijer sure thing, tomorrow when I get in to work. Iā€™ll PM you the script but post here too in case others want to read your follow up as well.

I am very happy to see this - I can tell you, if we had this in 4.x (I know itā€™s not coming to 4.x) Iā€™d make great use out of it, as Iā€™m currently trying to track down whatā€™s holding rogue textures in memory after their objects (and scenes) got destroyed. A very useful tool this new memory profiler will be!