Measuring memory impact: some scenes take up negative memory, what?

I’ve tried to measure the impact of loading some scenes in our game on memory consumption. The code goes basically like this:

Resources.UnloadUnusedAssets();
// wait for unload
var mem1 = Profiler.GetTotalAllocatedMemoryLong();
AssetBundle.LoadFromFile(...);
// actually a few bundles b/c dependencies
// wait for bundles
var mem2 = Profiler.GetTotalAllocatedMemoryLong();
SceneManager.LoadSceneAsync(...);
// wait for the scene
var mem3 = Profiler.GetTotalAllocatedMemoryLong();

Debug.Log($"{mem2-mem1}, {mem3-mem2}");

And sometimes the log shows negative numbers. I’m reasonably sure that nothing else gets loaded or unloaded in parallel to this - no scenes, no bundles, no objects destroyed or instantiated. How can loading a scene or a bundle reduce total memory allocation? Am I doing something wrong here? If so, what would be the correct approach to measuring memory consumption?
edit this happens in the player too, it’s not just some editor shenanigans.

Is it possible garbage collection occurred between the measurements?

Maybe, I guess, but I’m not measuring managed memory, I’m measuring “native” memory. Also, I’ve added

GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();

Before unloading and it does not change anything (it still does not guarantee anything, but probably GC shouldn’t happen again so fast?)

Also also, GC would be random, but I’m seeing the same negative numbers for the same scenes in multiple runs.

Only Unity, or people who have access to Unity’s source code, know exactly how they manage their memory underneath. Which most of us don’t have.

But we can theorize some things.

For starters… UnloadUnusedAssets doesn’t necessarily block until it’s done doing so. From what I can gather about Unity (though version to version may change this, again, we only can tell what Unity tells us or what we gleen from using it) is that it does this over time/asynchronously. You say in your comments “//wait for unload” but don’t necessarily say HOW you wait. How do you know you actually did wait? Unity doesn’t return a wait handle from UnloadUnusedAssets of any sort for you to do so. So it may not have completed until after you called any of your load methods.

Furthermore when you call load. Unity does a lot for you behind the scenes. It’s going to load everything and also resolve various dependencies for you that it knows it can. Thing is this work takes memory and it may need to allocate more memory than the resulting scene actually needs to do all the work. And then it likely cleans up after itself at the end of the load (again, not necessarily synchronously). You still leave comments that you “//wait for bundles” or “//wait for the scene”, but how do you do this? Comments don’t wait anything, and not sharing your code that does do this just obfuscates what is happening in your specific use case.

Well… that depends on what you’re actually looking for.

Mind you… GetTotalAllocatedMemoryLong is reporting the portion of the reserved memory that Unity is actually using at that moment. If that’s what you’re looking for… that’s what it gives you.

Mind you… that’s not all the memory that is actually reserved from the underlying operating system (or equivalent). That’s what GetTotalReservedMemoryLong does. Since Unity reserves large pools of memory from the OS that it then fills with your loaded assets.

But that’s still not ALL of the memory. That’s just the memory it reserves for dynamic usage. Things like where the program data itself is stored and what nots are not included in that number.

These numbers report the accurate current memory consumption of the specific parts of the system they represent (reserved for pools, and the amount of the pools actually used). Fluctuations in them are mainly the result of a lot of things happen between loads/unloads.

So when you ask “what would the correct approach to measuring memory consumption”…

I ask in return…

To which effect are you wonder?

Like are you trying to determine how much memory a specific scene takes up for documentation purposes? Well… that’s going to be hard to get an accurate telling (especially since you could have scenes that load things randomly based on some parameters… like enemy spawns that spit out random enemies, or save states that load/unload stuff based on the save state). But if you want a close estimate… load it up in a clean setting. Basically just spin up that single scene and nothing else and see how much it took up.

But why are you trying to determine this?

If it’s something like making sure you can operate within some system’s specs. Well… run it on that system. Play around in your game, do the activities that you know create the largest amount of memory, record the max through out all of this and check what that is in the end. Do this over and over and do a statistical analysis of your results. If you notice the game average around 800 megs, but peaks at 1.1 gigs… well, there you go. You have some numbers. You may then go out and slap on the side of your game “min requirements = X gigs of memory” based on those numbers + any buffer you feel safe saying. Cause yeah at the end of the day… you don’t know what your user has running on their system either that also takes up memory.

2 Likes

I’ve simplified the code a lot to only leave the relevant parts, but I do wait in the correct way. Specifically, this is a coroutine that does yield return null until the AsyncOperation.isDone becomes true.

I’m looking for the memory that the loaded scene takes up. More specifically, the internal Unity memory taken by GameObjects and assets, as opposed to the managed memory taken by my C# objects. I’m guessing that it should be reported by GetTotalAllocatedMemory, and maybe I’m wrong; but I’m pretty sure that GetTotalReservedmemory is not right (cause Unity will reserve more memory than it needs).

I appreciate you taking the time to write a long and thought-out answer, but that was quite condescending, to be honest. Of course we did run the game on the target platform (platforms, actually), and looked at the total memory consumption, and recorded it, and got some numbers that are unacceptable.
Now I need to reduce those numbers, and to do that I need to understand better where they come from and what it is exactly that takes up memory. We’ve used many different approaches for that, ranging from the built-in profiler to a bunch of ad-hoc measurements to hacking and rerouting Mono GC to record heap object allocations.
In this case, I want to match memory consumption to the scenes we load (as opposed to all the other things we might do such as loading bundles, instantiating more objects etc). And the result is surprising.

Yeah well… telling a PS4 owner “you need 8gb of free memory to play this game” is… less than helpful ((-8

Sorry if you find that sentence in my long post where I’m trying to be helpful condescending.

So you’re trying to fit within the spec of a specific console. (this is why I was asking what you’re attempting to do, we don’t know your situation)

So my answer still stands the same. The numbers aren’t going to be perfectly accurate. And the negatives just come from the way that behind the scenes a lot more goes on with memory management then you would expect. The closest you’ll get is a estimate since the actual memory a scene takes up can vary depending on what’s going on in the scene (unless you have very static scenes… which games tend not to have).

So yeah. As I said before. Create a clean scenario where you just load a scene individually. Check your memory there. Do this for every scene and get a rough idea of what each scene takes up.

You can even run the profiler (which can be ran remotely too) which can report more specifics on the memory usage.

And in the end you’ll still need to operate at min/maxes here (sorry I didn’t know what platform you’re targeting, it’s why I asked what you’re attempting to do). Because that’s how dynamic games work. You need to locate your mins and maxes. It’s not just when the level loads, it’s when you’re in the heat of a battle scene and a swarm of enemies are being spawned. Or it’s when you leave your game on for hours on end in the same level because the player is exploring, but lost, and you need to catch any errant leaks.

Anyways… back to your original question though of why you some times see negatives. It’s because memory management is more complex than just the assets in the scene. And sometimes during loading/and unloading more or less memory may be needed for various tasks since not just assets take up memory. So does the code that creates the code objects that do the tasks involved with loading/unloading the assets.

As a result you can’t rely on the method you’re using to get reproduceable exact results of memory as the dynamics of a game environment change. That is unless you created a perfectly determinate game (which is difficult in unity since it’s not designed to be determinate… but that isn’t to say it’s impossible, you can make specific aspects of your game determinate).

So instead I was attempting to offer other ways to approach your problem, but had to from a limited set of information as I do not know your game, your target, or your specific situation. This is what I was highlighting by asking what you’re actually doing… you intentionally left out information in your own code you posted, this doesn’t help, it hurts, as we now need to guess what you’re doing. We now have to guess why you’re seeing what you’re seeing. The memory differences could be because of how things work behind the scenes, or it could be a result of how you implement what you changed out for comments. We don’t know, there’s no way for us to know, since you hid that information.

It’s also why my posts become so long as I try to cover my bases… but I can’t cover them all without having a 3 year long post. So we end up missing things like you’re on PS4.

More information is better. Else you’ll get guesses like Gozzler gave, people who don’t even respond, or long blargh posts like my own. Which boils down to “that sucks that it’s not doing what you want, but maybe this will help? Not sure, but maybe?”

Anyways, to sum up, I wish you luck. And if you want, you can maybe give us some specifics about your game scenes and we may be able to give you an idea of what are hot spots you should look into when QA’ing your levels. But I also understand if you don’t want to share specifics of your game (or maybe even can’t because of contract or soemthing).