Skinned Mesh Material leak?

I’m not sure if this is a bug with the way we’re dealing with addressables or something more fundamental.

We’re on 2019.4.5 Unity and the latest addressables

We load a prefab that contains a skinned mesh and one material assigned to the mesh.

AsyncOperationHandle task = Addressables.LoadAssetAsync( // resourceLocation Object);

await task.Task

GameObject go = GameObject.Instantiate(task.Result);

… Wait a bit

GameObject.Destroy(go);
Resources.UnloadUnusedAssets();
GC.Collect();

We are always left with the object in memory on device and editor.

If we remove the material in the skinned mesh, the object is cleaned up as expected.

The material is not part of the bundle nor is it tagged as an addressable but is pulled in via reference i would assume when the bundle is built.

Is this expected? Is this a bug? Any proposed workarounds?

thanks

You need to call Addressables.Release(task).

Unfortunately i have tried all those solns and usually run into this:

Addressables.Release was called on an object that Addressables was not previously aware of. Thus nothing is being released

I also tried the Addressables.InstantiateAsync with the Release above and still no luck

Release/ReleaseInstance afaik should only be used if relying on Addressables ref counting which we are not right now.

Since you are using the LoadAssetAsync api, you need to hold onto the AsyncOperationHandle instance so that you can pass it into the Release api. If you just use InstantiateAsync, then you should use ReleaseInstance instead of Destroy, and you won’t need to hold onto the handle.

Correct, i get that but i am not seeing any different behaviour, i have tried all variants.
I am going to just pull out the code into a small project and test it with an asset in isolation as a sanity check. If it is still occurring, i’ll follow up.

thnx

Hmm so yes, my test project is working fine with the above code, will have to do more investigation in the main project, thnx for confirming clean up code

Ok so i have everything working as discussed, the issue comes to LoadAssetsAsync ( note: s )…
This will only return back 1 AsyncOperationHandle
We use the LoadAssetsAsync to rapidly load in a folder of assets but we use the assets at various times. Unfortunately there is no way i can see to get a per AsyncOperationHandle per asset via this api so i can release individual assets?

The documentation states that you can release on the Result but again, for the entire handle this is fine but i don’t want to release all 100 objects, only a subset

If i revert back to LoadAssetAsync, it works, but it is about 10x slower to load in

What’s the expected behaviour to release assets independently when loaded via LoadAssetsAsync or is that not possible?

If not then is the expectation that i manually filter out what should be loaded / unloaded via this call and release in one go?

thnx

What does your code look like to load them individually? It shouldn’t be that much slower.

I’m honestly not sure. If what you say is true about passing handle.Result to Release, then I would assume that you can pass each loaded object into Release.

This is the single LoadAsset way

foreach (var key in resourceKeys)
{
AsyncOperationHandle handle =
Addressables.LoadAssetAsync(locDb[key]);
await handle.Task;
}

vs

await Addressables.LoadAssetsAsync<UnityEngine.Object>(locDB, result => { // do somethin // ).Task;

I assume it’s just the fact it has to do more work per asset load vs some optimized path via LoadAssets

Unfortunately LoadAssetsAsync i don’t get an asyncHandle per object which i understand, so i have to group them together in a way to clean up the release later…

Well there’s your problem. You are waiting for each one to complete before starting the next. Change it to this and it should be fast:

var tasks = new List<Task<Object>>();
foreach (var key in resourceKeys)
{
    AsyncOperationHandle<Object> handle =
Addressables.LoadAssetAsync<Object>(locDb[key]);
    tasks.Add(handle.Task);
}
await Task.WhenAll(tasks);

This starts all operations running in parallel, then uses Task.WhenAll to wait for all of them to complete.

1 Like

doh, thanks for the catch