Correct way to remove MonoBehaviour/component from GO instantiated via Addressables

Alright, let’s say I have GameObject which I got by instantiating prefab via Addressables.InstantiateAsync(). So now I would like to remove some of MonoBehaviors from it. If I call Destroy() on that MB, will that mess up reference count for scene assets that might be referenced from that MonoBehaviour?

I can’t really think of use case where custom component will “hold” an asset but I’m curious what happens when I try to remove, let’s say, Image component which actually “holds” the instance of an image in memory?

I think if you manually destroy MB, any assets not references anywhere else will be unloaded from memory by the engine. But when you ready to remove the GO itself you should use Addressables.ReleaseInstance. This will unload the dependent AssetBundles (if any). Otherwise they will never be unloaded. Event Viewer helps to verify that things unload correctly.

Releasing assets after destroying MB makes sense, the GC will kick in and release the memory. But, as far as I understand it, If you instantiate GO via Addressables, it will do the reference book-keeping for that instance, but will also do that for all the MB and referenced assets in the underlying structure of that GO. So, if you change underlying structure without using Addressables API (e.g. by calling Destroy() on any MB), will the Addressables still hold reference to those assets even if they are not used by any MB?

I mean, those assets can be in the same Addressable group(Asset Bundle) with that prefab which you used to instantiate GO, so technically, it will not unload it from memory even if the ref count goes to zero, but let’s say we have each asset in separate Addressable Group for this case.

I’ll check the Event Viewer, I’m refactoring complex codebase so it will take some time untill I get there.

Addressables doesn’t do this kind of thing. Addressable assets can only have asset bundles as dependencies… how this can be a problem. Let’s say your addressable GO has a MeshRenderer that references some materials. These materials are placed in a bundle separate from the one with GO itself. If you destroy this MeshRenderer w/o releasing the GO, the only issue I can see is that the AssetBundle containing the material that could’ve been unloaded, will not be. But unless you’re loading LZMA bundle w/o using UnityWebRequest (local bundle), it’s memory overhead shouldn’t be a problem. Maybe it can be, if you have thousands of tiny asset bundles this way… depends on the project I guess.

The main thing is that Addressables doesn’t do anything special with parts of GameObjects at runtime. It only has a dependency tree baked into an asset catalog at build time which is used to know which assets to load from which bundle. But your materials will be unloaded from memory normally if you destroy all the MeshRenderers referencing them, for example.

This is the exact problem I’m trying to avoid and I created this thread for :slight_smile:

I’ve managed to find the thread where Bill actually explains this in more detail in his answers Release Asset vs Release Instance but I can’t tell for sure if the bundle containing the Texture actually gets unloaded or not.

As Bill mentioned: “if you have 2 parents, referencing the same other-thing. Load both, that other-thing has a ref-count of 2. Unload one parent, and that other-thing now goes down to one and does not get unloaded.” but I guess this ref-counting he referes to is ref counting mechanizm of Unity Engine itself.

As I understand it now, Addressables keep reference of asset bundles for the assets being loaded/instantiated, and eventually for other asset bundles for the assets referenced in original asset. But who does the ref counting for the assets itself? It looks like it is exclusive responsibility of Unity Engine but I can’t tell for sure.So Addressables use Unity Engine for ref counting so it can manage asset bundle ref-count based on it.

So we have 2 ref counting systems working toghether, one is in Unity Engine itself and another through Addressables API, but it’s not clear which one does what :frowning:

It is. As long as you pair your InstantiateAsync with Release (so you keep the GameObject itself and let Addressables destroy it) - there will be no issue.

As I tried to point out, Addressables is concerned with (as name implies) Addressable assets. Everything that’s added to AssetGroup and has an address. And downloading, loading/unloading AssetBundles that contain these assets. Assets in Unity can contain many Objects (as is the case with Prefabs). Under the hood, AssetBundle.LoadAssetAsync API is used. But as you probably know, this API doesn’t have any explicit ref counting or separate release mechanism. When you load an asset - UnityEngine.Objects are created and you use Object.Destroy to destroy them, just as you would with objects created with Instantiate for example.

I highly recommend looking at source code of InstantiateAsync & LoadAssetAsync in Addressables. The main takeaway is that you have to pair every Addressable operation with appropriate Release call (unless there is an autoRelease parameter used, in some methods). If you loaded something from Addressables, you release it back. But in your specific case, there should be no issue as long as you don’t destroy the GameObject itself that you obtained from InstantiateAsync and instead Release it.

Another way to look at it:

  • Addressables at Runtime is basically a whole lot of helper code around AssetBundles and UnityWebRequest.
  • Addressables in Editor is a whole lot of helper code around Scriptable Build Pipeline (for producing AssetBundles) + some ScriptableObjects (for configuration) and custom windows.
1 Like

Alright, I guess I have more clear pucture now of how it works. Thanks for taking the time to discuss this.

Btw, I finally managed to wrap up some things and looked into Event Viewer. Here is one interesting observation. I use LoadAssetsAsync to load multiple materials in single operation. When I tried to release materials individually, it did not work, complaining about asset not registered in addressable system. So I checked the docs and found out that you need to release either operation handle or its result in order for the asset to be released, which totally makes sense. So I tried releasing the whole list, and it worked, materials dissapeared from event viewer. But then, I tried to break it by modifying list of materials before releasing it, so I added new material like so: materials.Add(new Material(Shader.Find(“Specular”))); and tried to release the list again and it also worked. Since materials loaded in original operation are “registered” as dependencies, I guess the system knows how to release them, but I wonder what happened with the additional material I’ve created. It does not show in memory profiler so I guess it just gets unloaded by the engine because thare are no references to it any more. Intersting thing is that the system does not complain about trying to release this asset when I release the whole list.