- Home /
Asset is not unloaded even if GameObject destroyed
I believe that this question was discussed many times, but still.
Consider an example: We have some kind of singleton class which exists all the time. It has public GameObject MyGameObject;
We load scene1. And set MyGameObject = GameObject.Find("Whatever"); Then we load scene2.
Now Debug.Log(MyGameObject == null) will say "true". I suppose somekind of wrapper still exists, which knows that this GameObject doesn't exists anymore. We can't even find it using FindObjectsOfTypeAll (and we can't find source asset). So Unity knows that object was destroyed, but it still keeps texture alive! (after calling UnloadUnusedAssets of course).
So until we excatly set MyGameObject = null; we can't get rid of texture it uses in our memory.
This is very simple example, in reality everything can be very unobvious. We can reference another class, which can refenrece gameobject which was destroyed and it's component can has delegate in which closure still exists local variable of another gameobject and etc.. I understand that this is our problem and we should keep eyes on our code, but I'm really interested in reasonable explanation why is it so?
Ins$$anonymous$$d of checking for null-value, you should explicitely release any references when loading a new level or destroying the object:
void OnLevelWasLoaded()
{
this.$$anonymous$$yGameObject = null;
}
Yes, I mentioned it. The whole question is not about how to handle with it, but why does it happen?
It happens because .Net use a reference implementation. If an object is referenced by at least one other object then it cannot release any memory (it is similar to reference counting in C++ but is done in a different manner).
To prevent this, you should make sure that you release all references to any resources when destroying an object. The best way is to use OnDestroy
:
public class Example : $$anonymous$$onoBehaviour
{
public Texture2D textureRef;
public OtherScript scriptRef;
public GameObject objectRef;
void Start()
{
this.objectRef = GameObject.Find("objectname");
this.textureRef = FindObjectsOfTypeAll(typeof(Texture2D))[0];
this.scriptRef = objectRef.getComponent(typeof(OtherScript ));
}
void OnDestroy()
{
this.objectRef = null;
this.scriptRef = null;
this.textureRef = null;
}
}
And in your singleton object:
void OnLevelWasLoaded()
{
Resources.UnloadUnusedAssets();
System.GC.Collect();
}
But he said that Debug.Log($$anonymous$$yGameObject == null) outputs true. There must be some other object referencing $$anonymous$$yGameObject or textures it is using. Because if it is eligible for GC and it is the only object referencing a texture this texture should be GC'ed too.
$$anonymous$$aybe something is done wrongly is his project because I did use this method in an iOS project and the resources were unloaded as expected.
Answer by valyard · Nov 07, 2012 at 01:54 PM
Unity doesn't unload textures from memory if objects using them are destroyed. It was mentioned in some of the talks from Unite'12. To get rid of them you
either have to call Resources.UnloadUnusedAssets
or change level
In your case you sya that you use Resources.UnloadUnusedAssets and load another level. So you use both of them and textures are not unloaded.
First of all why are you sure that textures are still there? Are you sure that this gameobject doesn't have don't destroy when loading another scene flag set? Are you sure that it is not referenced by any behavior with such flag?
Otherwise it looks like a bug.
I believe that changing scene and destroying the object doesn't make any difference. Only setting all references to null will allow you to get rid of texture.
Everybody can check this simple case on their own. Just output FindObjectsOfTypeAll(typeof(Texture2D)) to check if it's still there.
I understand how to deal with that problem. But it is really very unobvious and I hope to know why it happens.