Hey guys.
After I created recently a custom automated memory leak analyzer tool for a number of large projects in production, I noticed how many leaks come from how Object’s ==operator is unable to detect a destroyed MonoBehaviour, or Object, that is still alive on the native side.
I went through a number of Object ==operator, CompareBaseObjects() and IsNativeObjectAlive() refactoring ideas – in addition to a data driven test that measures the performance of each variation, since the ==operator is ultra hot path.
You’ll find attached two folders that you can add to a project, in order to
a) SimpleLeakTest: It shows a common leak related to static fields and the fake null problem, with a common solution.
b) EqualityBenchmark - with a few alternative versions of Object ==operator, CompareBaseObjects() and IsNativeObjectAlive() which are time measured & tested, against the original Object ==operator. I refer to this original Object versions:
It ends with
I prefer Solution G, since it has less changes, but up to the team, of course. See attachment!
Goal: The ultimate goal of the proposal is to improve “fake nulls” of Unity’s Object, to limit situations like these.
And please correct me, if I’m wrong.

Archive.zip (61.8 KB)
I didn’t download the file, and not many people will download a file onto their machines without being able to review the code first. It would be better to have a GitHub repository for the project.
But saying:
is fundamentally wrong. The == operator doesn’t exist because the objects remain alive on the native side. The exact opposite is happening: the objects on the managed side remain alive after the corresponding objects on the native side have already been destroyed.
Native side objects can be destroyed whenever the programmer needs them to, because they are implemented in C++ and can be destroyed independently of the managed objects. The issue is on the managed side, where objects cannot be destroyed at will, instead they have to be collected by the garbage collector. So Unity can end up in a situation where an object has been destroyed on the native side while its managed counterpart is still alive. That’s why the == operator is overloaded: to check if the corresponding native object has been destroyed.
I didn’t run the project to see why you’re experiencing memory leaks, but they certainly aren’t caused by the overloaded == operator. There aren’t in Unity any objects that are alive on the native side but destroyed on the managed side.
That is not (necessarily) a leak! The object has been removed internally but the C# wrapper remains. It’s not leaked in that sense because it’s absolutely valid for it to continue living on.
For instance in a “lazy pool” one might cache such GameObject references and not care about whether they have been destroyed or not. But the next spawn might find the next free location, checks if it is null, which it is (due to Unity’s == override), and then assigns the new instance to it, thereby releasing the C# object into garbage collection. And at the latest on a scene change that collection would be cleared and thereby all remaining ghost C# objects can be released by the GC.
Needless to say, that is not the best approach but these are in real projects and don’t cause any issues.
It’s also not something I would task runtime code with. We have powerful static analyzers that point out these issues even without running the application. One just has to use them. And the people who don’t use static analyzers are the same who won’t use your solution .. lile > 90% overlap (guess). 