Memory constraints in Unity WebGL can restrict the complexity of the content you can run.
WebGL content runs in the browser. The browser allocates the memory in its memory space that your application needs to run your content. The amount of available memory varies depending on:
There are several areas where Unity WebGL content needs the browser to allocate significant amounts of memory.
Unity uses a memory heap to store all Unity engine runtime objects. These include managed and native objects, loaded Assets, Scenes and shaders. This is like the memory that Unity Players use on any other platform.
The Unity heap is a contiguous block of allocated memory. Unity supports automatic resizing for the heap to suit the needs of the application. The heap size expands as an application runs, and can expand up to 2GB. Unity creates this memory heap as a Memory object. The Memory object’s buffer property is a resizable ArrayBuffer that holds the raw bytes of memory accessed by WebAssembly code.
Automatic resizing of the heap can cause your application to crash if the browser fails to allocate a contiguous memory block in the address space. For this reason, it is important to keep the Unity heap size as small as possible. Therefore, be mindful when you are planning the memory usage of your application. If you want to test the size of your Unity heap, you can use the Profiler to profile the contents of the memory block.
When you create a Unity WebGL build, Unity generates a .data
file. This contains all the Scenes and Assets the application needs to launch. Because Unity WebGL does not have access to the real file system, it creates a virtual memory file system, and the browser unpacks the .data
file here. The Emscipten framework (JavaScript) allocates this memory file system in the browser memory space. While your content runs, the browser memory keeps the uncompressed data. To keep both download times and memory usage low, try to keep this uncompressed data as small as possible.
To reduce memory use, you can pack your Asset data into AssetBundles. AssetBundles allow you full control over your Asset downloads. This means that you can control when your application downloads an Asset, and when the runtime unloads it. Unloading unused Assets frees up memory.
AssetBundles download directly into the Unity heap, so these don’t result in extra allocation by the browser.
Enable Data Caching to automatically cache the Asset data in your content on the user’s machine. This means you don’t need to re-download that data during later runs. The Unity WebGL loader implements Data Caching with the IndexedDB API. This option allows you to cache files which are too large for the browser to cache natively.
Data caching enables the browser to store application data on the user’s machine. Browsers often limit the amount you can store in their cache and the maximum file size that can be cached. This is often not enough for an application to run smoothly. Therefore the Unity WebGL loader implements Date Caching with the IndexedDB API. Instead of storing the content in the browser cache, Unity stores the data in the IndexedDB.
To enable the Data Caching option go to File > Build Settings > Player Settings > Publishing Settings.
Your server can emit the Large-Allocation HTTP header for your content. This tells supported browsers (currently only Mozilla Firefox) about your memory needs. This information allows the supported browser to spawn a new process using unfragmented memory space. The browser can also do extra housekeeping to make sure the large allocation succeeds. This can solve issues where the browser runs out of memory when trying to allocate the Unity heap. This is especially important on 32-bit browsers.
Garbage collection is the process of locating and freeing up unused memory. Once the garbage collector collects unused memory it reallocates it inside the Unity heap.
For an overview on how Unity garbage collection works, see Automatic Memory Management. WebGL garbage collection runs when the stack is empty. The stack is a part of the Unity heap but not the heap itself. This usually occurs after every frame. This is different from the garbage collection process on other platforms, where the collector pauses all running threads so it can inspect the stack. This is not possible in JavaScript. You can debug the garbage collection process using the Unity Profiler.
On most other platforms garbage collection pauses all running threads. The garbage collector will then inspect their stacks and register for loaded object references. This is not currently possible in JavaScript. For this reason, the garbage collector will only run in WebGL in situations where the stack is empty. This currently happens once after every frame.
Due to this, the following code would fail running on WebGL. This is because the collector does not get a chance to run the garbage collector between iterations of the loop. This means the garbage collector cannot free up memory that the intermediate string objects use, and is likely to run out of memory in the Unity heap.
``` string hugeString = "";
for (int i = 0; i < 100000; i++)
{
hugeString += “foo”;
} ```
Updated WebGL loader introduced in 2020.1