Can I see non-Monobehavior objects in the memory profiler?

Is there I way to see my non-monobehavior objects in the Memory Profiler? I can find all scripts that implements Monobehaviour but not my non-mb classes.

No you can’t. This information is only known to the GC. You should also keep in mind that the Memory you see for MonoBehaviour classes is mainly the native size. Unity is written in C++, not in C#. See the user manual about the Memory Profiler. Your “Non-MonoBehaviour classes” are simple .NET / Mono classes which are subject to garbage collection. MonoBehaviours are not. (well, maybe partly).

The GC tracks every bit and byte that is allocated on the managed heap. If an object is no longer referenced it will be garbage collected. This is also true for object chains, trees or rings. So if you have a variable to a class A, class A holds a reference to a class B instance and B references a class C instance. If you set your variable (that holds the A instance) to null or a different value the class is no longer accessable and will be garbage collected some time in the future. Along with A it will also collect B and C.

This object tracking is not accessable. It soley belongs to the GC. There are basically three ways why an object is still alive:

  • either it’s referenced from some static / global variable.
  • it’s referenced from a MonoBehaviour / ScriptableObject since those also persist.
  • or it’s referenced from the current stack frame. So a reference that only “lives” inside the currently executing method.

Note that case 1 includes all UnityEngine.Object derived classes as those are the “native object” that Unity already tracks on the native code side.

Case 1 and 2 could be figured out by using reflection and iterate through all classes in all loaded assemblies. Check all classes for static fields and traverse all referenced classes the same way. Specifically for UnityEngine.Object derived classes (which includes MonoBehaviour and ScriptableObject) you can use Resources.FindObjectsOfTypeAll to get a list of all objects that are currently loaded.

Case 3 is basically impossible to determine from an outside perspective. Note that even coroutines do not really belong into this case (they are actually objects), it’s also not possible to access them since the references to those classes are stored on the native side and you can’t query them in any way. So it’s possible to have an endless running coroutine that holds a huge tree of objects which aren’t referenced somewhere else. Those can’t be “detected” that way.

When you iterate through the object tree you have to be careful. First. since you have to store your results somewhere you should exclude your class from being traversed. Even more important is that you have to remember which object you have already checked. If you do not a circular reference would send your code into an infinite loop. Make sure you only traverse public and private fields and not properties. You may also run into problems with some built-in components and their private fields.

Though following those steps you can “manually” create a “snapshot” of all classes that are currently in memory and you have a chance to access them.