I am aware I need to do my own memory management when it comes to GameObject in unity. That is, for every GameObject I Instatitate() or “new GameObject()”, I have to run UnityEngine.Object.Destry(thegameobject);
I recently encountered ScriptableObject.
If I create a ScriptableObject using ScriptableObject.CreateInstance(), do I also need to call, UnityEngine.Object.Destry(thescriptableobject); ?
Case example:
I create Tilemaps and Tiles via code. The contents of the Tilemaps change as the game runs. I create a Tile using ScriptableObject.CreateInstance(); I load a sprite from a SpriteAtlas and assign it to the tile. I set the tile to the tilemap using SetTile().
Later, the tile might change (I can simply change the sprite). Or I need to “delete” the tile. Or I need to “delete” all of the tiles in that particular tilemap.
Simple way to delete tile/all tiles:
tilemap.SetTile(location, null); // Does unity “free” the tile ScriptableObject?
or
tilemap.ClearAllTiles(); // Does unity “free” the tile ScriptableObjects?
Manage it myself:
oldTile = tilemap.GetTile(location);
tilemap.SetTile(location, null);
UnityEngine.Object.Destry(oldTile);
or
int i = tilemap.GetUsedTilesCount();
Tile[ ] usedTiles = new Tile*;* tilemap.GetUsedTilesNonAlloc(usedTiles); foreach (Tile t in usedTiles) UnityEngine.Object.Destroy(t); I’m guessing I need to call Destroy() since ScriptableObject (and everything inheriting from it?) has OnDestroy(). Also, what about Sprite.Create() or “new Texture2D()”? Are there any other common ones I should be aware of (I’m doing 2D development, but someone reading this might be doing 3D.) Thanks!
You know what? That’s a good question.
I think they behave exactly the same as MonoBehaviour in this regard.
It can’t die unless Destroy is called, because it doesn’t belong fully to C# side anyway.
I can’t tell if using CreateInstance changes anything.
Calling destroy is definitely a way to unload a ScriptableObject similar to how you have to destroy a GameObject or components.
But you don’t necessarily have to call destroy. You just have to not be using it in any way and then allow Unity to unload unused assets through some means.
This can be done simply by calling “Resources.UnloadUnusedAssets” or by loading a new scene, reloading the same scene, or really any transitionary element where Unity unloads assets.
For example here is a simple test script:
public class zTest02 : MonoBehaviour
{
private const int COUNT = 10000;
private List<TestScriptableObject> _lst = new List<TestScriptableObject>(COUNT);
private List<TestScriptableObject> _hold = new List<TestScriptableObject>(COUNT * 10);
private bool _ignore = false;
private void Start()
{
Debug.Log("SCENE STARTED.");
}
public void Update()
{
if (_ignore) return;
if(Input.GetKeyDown(KeyCode.Space))
{
if(_lst.Count > 0)
{
this.StartCoroutine(this.WaitAndClear());
}
else
{
Debug.Log("CREATING...");
for(int i = 0; i < COUNT; i++)
{
_lst.Add(ScriptableObject.CreateInstance<TestScriptableObject>());
}
}
}
else if(Input.GetKeyDown(KeyCode.Return))
{
Debug.Log("RELOADING...");
SceneManager.LoadScene(0);
}
}
private System.Collections.IEnumerator WaitAndClear()
{
_ignore = true;
Debug.Log("PURGING...");
_hold.AddRange(_lst.Take(COUNT / 10));
_lst.Clear();
yield return new WaitForSeconds(1f);
Resources.UnloadUnusedAssets();
Debug.Log("PURGED.");
_ignore = false;
}
public class TestScriptableObject : ScriptableObject
{
public double A;
public long B;
public int C;
public Vector3 D;
public Vector4 E;
}
}
If you were to run this while the profiler was going and click on the memory profile. You’ll see a section that lists off the object count:
Now if you press SPACE, that “Object Count” at the bottom should go up by 10000.
Press it again and it’ll drop by 900; since I “hold” 1000 of them, but purge the other 9000. I do this to demonstrate objects that have references held don’t get unloaded.
Press it again, and the count goes up by 10000 again.
Press Return/Enter, and the scene reloads triggering a different way things get unloaded. And again the count drops… including those that were placed in the “_hold” list because the zTest02 script was also unloaded.
…
This also applies to your tile’s and textures, and well anything that is a UnityEngine.Object. The unloading of unused assets will purge anything that no reference exists to.
Mind you if you have statics/singletons/managers that persist and hold references to these things. Well, they’ll persist!
Also just because the unity side object was removed. This doesn’t necessarily mean the C#/mono side object was also removed. That will remain until GC is collected (which happens automatically periodically, or can be called manually via System.GC.Collect()).
Also note that if you reference a thing that references a thing that references a thing… then the reference exists. Like if you create a texture that is attached to a shader that is attached to a renderer. Well yeah, that texture is going to persist since something is referencing it and that reference chains all the way back to whatever reference you’re holding. For it to get removed you’d have to have no refs to the texture and shader as well as destroy the renderer (by destroying it, its gameobject, or unloading the scene they’re in).
I don’t know about other people. But I sometimes do if it’s a long lived scene and I’ve recently loaded up a large amount of assets to then purge them.
Negative side effects would exist if you rely on some object existing even though it’s not referenced by anything. That’s definitely a weird situation to have… but it’s a situation that could exist.
One of these sorts of situations is for example…
You have a ScriptableObject class that is a singleton.
You’ve placed the instance in a Resources path.
Instead of loading and sticking in some static ‘instance’ variable/field, you instead load it every time you try to access it.
Calling Load will just return the same instance if it’s already loaded.
You modify the singleton’s state (especially any state not serialized), which is fine because it’s still persisting.
You unload resources.
You access said singleton again and its state is reset.
It’s a weird scenario for sure, but it’s a scenario none the less. If you rely on such situations you could have negative side effects.