GameObject.InstantiateAsync randomly causes runtime to freeze entirely

Hello!
I’m having an issue where eventually, seemingly randomly, InstantiateAsyc will completely freeze the game. The code is for instantiating new locations in the game as you walk around the world.

TLDR:

  • InstantiateAsync calls are running from inside the OnUpdate method of an ISystem
  • They are instantiating prefabs loaded from Addressables
  • The freeze happens between the InstantiateAsync call and the launch of the completed Action

Details:
Location requests are created as you walk around the world and it’s detected that a location needs to be spawned. The freeze seemingly happens randomly, sometimes on the first time it’s loading a location, sometimes later when loading previously loaded/destroyed locations (for ex if you walk back and forth). Currently no Addressable handles are released. It happens both in the Editor and in a build.

The code below has been simplified, stuff that comes from other sources are in <>
When creating a location request, we launch the Addressables loading like so:

                AssetReferenceT<GameObject> locationReference =
                    new AssetReferenceT<GameObject>("Baked/" + <locationID> +".prefab");
                locationReference.LoadAssetAsync();

This location reference is then stored in the locationRequest as locationRequest.prefabReference.
Then, we iterate on the requests:

        foreach (var locationRequest in locationRequests)
        {
            if (!locationRequest.prefabReference.OperationHandle.IsDone ||
                locationRequest.prefabReference.Asset == null)
                continue;
            
                Location location = ((GameObject)locationRequest.prefabReference.Asset).GetComponent<Location>();
                // Make a copy of the local variables used inside the lambda function
                AssetReferenceT<GameObject> prefabReference = locationRequest.prefabReference;
                Location currentLocation = location;
                
                Debug.Log("[InstantiateAsync]: launching instantiation: " + currentLocation.gameObject.name);
                GameObject.InstantiateAsync(location.gameObject, 1, <pivotTransform>, <position>, <rotation>).completed += (operation =>
                {
                    try
                    {
                        AsyncInstantiateOperation asyncOperation = operation as AsyncInstantiateOperation;
                        if (asyncOperation != null && asyncOperation.Result.Length > 0)
                        {
                            <locationStore> = ((GameObject)asyncOperation.Result[0]).GetComponent<Location>();
                            Debug.Log("[InstantiateAsync]: finished instantiation: " + (GameObject)asyncOperation.Result[0]);
                        }
                        else
                        {
                            Debug.LogError("Location async instantiation failed for " + prefabReference.Asset.name);
                        }
                    }
                    catch (Exception e)
                    {
                        Debug.LogError(e);
                        Debug.LogError("Exception happened during Location async instantiation for " + currentLocation.gameObject.name);
                        throw;
                    }
                });
                <removes request from list>
             }

After walking around for a while, the game freezes. In the log, there are no errors, and after multiple “launching instantiation” and “finished instantiation” logs, it freezes after a “launching instantiation”.

Anyone has any idea of why this could be happening?
Any help would be appreciated. Thanks!

Have you been able to narrow it down to a specific object? You could log location.gameObject.name for instance. If it happens to the same object it would seem possible that it runs something in Awake etc that might cause it to spin into an infinite loop.

Try attaching the debugger when it freezes, then set breakpoints inside any loop that may spin out of control.

The only other curious thing is that I would avoid using a lambda as a callback in runtime code (it allocates):

GameObject.InstantiateAsync(location.gameObject, 1, <pivotTransform>, <position>, <rotation>).completed += (operation => ...

Rather keep the object around and use a dedicated method:

var op = GameObject.InstantiateAsync(location.gameObject, 1, <pivotTransform>, <position>, <rotation>);
op.completed += TheCompletionCallback;

I find this also improves code readability quite a bit because it does not intertwine completion code in between the instantiation code (above and below the inline function).

A profile capture could be revealing.

1 Like

Try move this out of isystem and into systembase. Pretty sure events break in isystem (unreferenced struct) and gc has maybe cleaned up the event.

Update on this: I created a test scene to isolate the problem more.

  • I removed the Addressable loading to make sure it wasn’t related to Addressables
  • I placed the code outside of ECS Systems
  • I created a coroutine activated by the click of a UI button that instantiates a prefab a given number of times, and played around with the amount instantiated per frame as well

My findings:

  • The main finding is that the bigger the prefab, the earlier it freezes. I had no problem whatsoever instantiating props, but our locations are huge (prefabs are between 10-45mb) with tens of thousands of objects inside.
  • The number of InstantiateAsync calls per frame also matters → Even with the biggest location, if I made sure to do yield return null before calling InstantiateAsync again, the freeze was gone. With 2+ calls it would freeze after ~2 frames

In our game we have different systems that take care of instantiating props and locations separately, but my workaround for the moment will be to centralize all requests and make sure to call InstantiateAsync once per frame. This is not ideal of course, and probably should be investigated by Unity.

A profile capture could be revealing.

1 Like

as the entire Unity Editor freezes and must be killed, a profile capture is not possible.

Run in a debug session, then, when the editor freezes, hit pause in your IDE’s debugger and see where it froze.

You probably have a tight endless loop somewhere where the async operation hits the main thread, probably in Awake() or Start() or any other Unity Event of the object (or in a System)

Unfortunately not very informative. The debugger highlights the InstantiateAsync call, but the stack just shows it went into Native code

I would check RandomPrefabs Awake and OnEnabled Methods and try to see if there’s any code that might loop indefinitely.

Could you make a public repro instead?

Update: I just found that this was found internally and fixed on Unity 6 :sob:

Scripting: Fixed freeze in InstantiateAsync with very large objects. (UUM-95656)

Oh geez… Here’s hoping that this is backported to 2022 LTS

1 Like

It has just landed on 2022.3.59. Cheers!