Texture2D does not delete itself. Any new Texture2D objects you make must be processed with UnityEngine.Object.Destroy(texture2D) by the end of their lifespan or they will live in memory forever.
I thus was interested to build a class that will automatically destroy the Texture2D it contains when it goes out of scope.
ie.:
public class Texture2DManaged
{
public Texture2D texture2D;
public Texture2DManaged(int width, int height) {
texture2D = new Texture2D(width, height);
}
~Texture2DManaged() {
Debug.Log("DESTRUCTOR RUN");
UnityEngine.Object.Destroy(texture2D);
texture2D = null;
}
}
Then, in an Update() loop on any game object to test:
if (Input.GetMouseButtonDown(0)) {
Texture2DManaged texture = new Texture2DManaged(5000, 5000);
//UnityEngine.Object.Destroy(texture.texture2D);
}
This code as is runs the DESTRUCTOR RUN debug code, but yet memory use continually rises in the Profiler when you click. By contrast, if you enable the Destroy line in the Update on click loop, this memory rise stops.
Ie. The Destroy line works correctly on mouse click but not correctly in the destructor. Any thoughts or solutions?
Sorry but this behaviour is true of ANY UnityEngine.Object.
Since anything inheriting from that class lives in both the C# and C++ sides of Unity, thus you are responsible for destroying it when no longer required.
Destructors/Finalisers do not work on UnityEngine.Objects, or at least not in the way you want them to.
The managed Garbage Collector doesn’t call destructors as soon as an object runs out of scope but whenever garbage collection happens. Your code mostly allocates native and only a very small amount of managed memory so it might run a while and not run out of managed memory and thereby trigger the GC.
If you want to have that holder object destructed on leaving the scope, an IDisposable pattern and a using scope might work better for those purposes.
Finalizers are called by the garbage collector runs on a separate thread, not in Unity’s main thread.
But unity engine code can not be called from a separate thread. It will throw a “UnityEngine.UnityException”. And Unity will not catch and log exceptions running on separate threads. So you cannot see the exception message in Unity Editor console log window. But if you have a try..catch block in your finalizer and output the exception in seperate file you will see that.
Good point, the object destruction triggered from the finalizer would have to schedule that cleanup work in such a way that it would happen on the main thread. So using a IDisposable pattern where you explicitly dispose of it in the same scope and only destroy it in the Dispose() method and not the finalizer would be better and everything else just getting relatively complex just to save you from having to remember to manage its lifetime explicitly.