Is a unity object really destroyed if its destructor was never called?

Ok, this might be a little complicated to explain so bear with me. I have made a very simple test environment with two test scripts that allows me to determine if a particular activity will cause a unity object to leak even though Destroy has been called on it.

I have a script called MyScript which I attach to an empty GameObject. Then I have a different script called TestScript placed on a different GameObject (Main Camera in my case) which grabs a reference to the first GameObject(i.e MyScript). If I hit the Destroy button then you will notice that an “OnDestroy” log statement will appear in the console window, followed by a “Destructor” log statement shortly after. To me this verifies that the object was completely destroyed and garbage collected. Please let me know if that is not correct.

Now, if I un-comment the following line of code //dr = m_go.GetComponent(); inside TestScript and explicitly grab a reference to the MyScript component on the GameObject, then I hit the Destroy button something different happens. The “OnDestroy” still gets called by Unity but we get no “Destructor” log statement. Does this mean that the object MyScript was not garbage collected? It would make sense to me that was the case since I’m holding a reference to it, but I’m not entirely sure since Unity seems to null out my reference to the component. You can verify this by hitting the Check button which will output null.

The reason I ask is because our games are riddled with references to the components which are retrieved through public inspector properties of a particular type, instantiating of a prefab through a script type, or just simply retrieving the component from the GameObject, and when we want to destroy the object we call Destroy(MyScript.gameObject) which produces what seems to be a leak if we still had a reference to the script.

Some of you may already be familiar with this, however I was not, so I would appreciate any input/feedback/clarification you guys have. Here are the scripts:

using UnityEngine;
using System.Collections;

public class MyScript : MonoBehaviour
{
    public TestScript hold;

    private void Start()
    {
        //hold.Del = Handler; //Does not call destructor.
    }

    private void Handler()
    {
        
    }

    private void OnDestroy()
    {
        Debug.Log("bye :(");
    }

    ~MyScript()
    {
        Debug.Log("Destructor");
    }
}

using UnityEngine;
using System.Collections;

public class TestScript : MonoBehaviour
{
    public TestDelegate Del;

    private GameObject m_go;

    private MyScript dr;

    private void Start()
    {
        m_go = GameObject.Find("GameObject");
        //dr = m_go.GetComponent<MyScript>(); // Does not call destructor
    }

    private void OnGUI()
    {
        if (GUI.Button(new Rect(0, 0, 100, 50), "Destroy"))
        {
            Destroy(m_go);
        }

        if (GUI.Button(new Rect(0, 50, 100, 50), "Check"))
        {
            Debug.Log(dr);
        }
    }
}

public delegate void TestDelegate();

From everything I’ve been reading about .Net/Mono memory management, it seems that a memory block is automatically disposed of by the garbage collector when there are no more references to it - thus, if you get a reference to MyScript, the garbage collector won’t free it. If you want to know more details about .Net memory management, take a look at this article; there’s also a relationship between a destructor and the Finalize callback function that’s called by the garbage collector when disposing of an object (see this topic). To further complicate matters, Unity does some extra magic between Destroy and the actual object disposal, thus other surprises may arise from your tests.