Loading and unloading asset could cause memory problem if we are not able to handle it carefully. Here you could find the problem we might meet, the solution and the reason why such a problem happens.
Texture assets remain in memory after scene changes
When player is in scene A, those character textures that he has checked would be loaded, which is normal. But when he moves to scene B, those textures still remain in memory. To see memory profiler, go to Window -> Profiler, click memory panel, then chose detailed option. Run the game, click “Take Sampler:Editor”, then we could see all asset loaded in memory.
The thing we need to do is set texture to null when dynamic card is destroyed (onDestroy()). Then call Resources.UnloadUnusedAssets() when the scene ends or changes as we use Resources.Load() to get the 2D texture.
Usually we want to use Resources.UnloadUnusedAssets() to unload asset loaded from resource.And we have to make sure there is no reference to the asset itself before calling Resources.UnloadUnusedAssets(). In the example, the game object should be destroyed when scene changes.
However, Destroy() function does not work in a proper way. In detailed, a lot objects in Unity, to be more precisely, all that are inherited from UnityEngine.Object, consists of two parts: a managed object and a native code c++ class in the engine itself. In the managed environment we only “see” the managed object that is “linked” with the native part. This is where two totally different worlds collide. When you call destroy on the managed object, Unity will destroy the native code part of the object. But the managed part of the class is still there! Unity will flag the class internally as “destroyed”.
In short, Unity uses a “trick” to make the managed part “pretend” to be null when the native part is destroyed. In the example, the character texture in dynamic card (2D texture derived from object) has not been destroyed correctly. There is still a reference to the texture asset loaded from Resources and Resources.UnloadUnusedAssets() could not unload it.
Therefore, in order to destroy the object properly, we have to set the asset to null, like it mentioned above. In this way, there is no reference to the texture asset loaded from Resources. And Resources.UnloadUnusedAssets() could work correctly at last.
Atlas Memory Problem
We probably meet the problem that atlas still remains in memory after scene changes. This is not what we want to see because usually the size of atlas is big. In order to solve that,we might need to do following things:
- Call UIDrawCall.ReleaseInactive() or ReleaseAll()
- Then call UnloadUnusedAssets().
Also, if the scene we work on has the static object, make sure object set to null before it is destroyed (including the texture, material and etc). Usually if we see ManagedStaticReference in “Reference by”, then we have to be careful with that.
Moreover, think twice before working on NGUI object, as NGUI object would have the reference to atlas. If the object is not destroyed correctly when scene changes, it could cause atlas remain in memory.
Currently even Unity community could not provide available solution to atlas unloaded problem. And it is possible that NGUI or Unity itself still has some bugs. We have to rely on ourselves now.
To sum up, we need to check following issues if we meet similar memory problem:
1.Keep track of the asset loaded from resource, make sure it have been set to null when it is destroyed.
2.Be careful about the assignment operator. For example:
Object obj = Resources.Load(“MyPrefab”);
GameObject instance = Instantiate(obj) as GameObject;
We also have to destroy obj if we want to call Resources.UnloadUnusedAssets() to unload the “MyPrefab” asset. This is why in 1 says “keep track of the loaded asset”.
3.Make sure Resources.UnloadUnusedAssets() is called. Even though this function should be automatically called when scene changes ( I have tested it and it is true). It is better call Resources.UnloadUnusedAssets() explicitly just in case.
4.Make sure static object has been cleared up.
5. If the asset still could not be unloaded, try calling System.GC.Collect(). Although it might solve the problem, it is not recommended.