Unity WebGL 中的内存可能成为限制可运行内容的复杂性的制约因素,因此我们希望在此提供一些关于如何在 WebGL 中使用内存的说明。
WebGL 内容将在浏览器中运行,因此浏览器必须在浏览器的内存空间内分配内存。可用内存量可能会有很大差异,具体取决于所使用的浏览器、操作系统和设备。决定因素包括浏览器是 32 位还是 64 位进程,浏览器为每个标签使用单独的进程还是让您的内容与所有其他打开的标签共享内存空间,以及浏览器的 JavaScript 引擎需要多少内存来解析您的代码。
Unity WebGL 内容在几个方面需要浏览器分配大量内存:
This is the memory Unity uses to store all its state, managed and native objects and currently loaded assets and scenes. This is similar to the memory used by Unity Players on any other platform. You can configure the size of this in the Unity WebGL Player settings. You can use the Unity Profiler to profile and sample the contents of this memory. This memory will be created as a TypedArray of bytes in JavaScript code, and requires the browser be able to allocate a consecutive block of memory of this size. You will want this space to be as small as possible (so that the browser can allocate it even if memory is fragmented), but large enough to fit all the data required to play any scene of your content.
创建 Unity WebGL 构建时,Unity 将写出一个 .data 文件,其中包含您的内容所需的所有场景和资源。由于 WebGL 没有真正的文件系统,因此在您的内容启动之前将下载此文件,并且在内容运行的整个过程中,未压缩的数据将保存在连续的浏览器内存块中。因此,为了缩短下载时间和降低内存使用量,应尽量保持这些数据尽可能小。请参阅文档的减小文件大小页面以了解如何优化资源的构建大小。
为减少加载时间和资源使用的内存量,可采取的另一项措施是将资源数据打包为 AssetBundle。通过这种做法,您可以完全控制何时需要下载资源,并可在不再需要它们时卸载它们,从而释放它们使用的内存。请注意,AssetBundle 将直接加载到 Unity 堆中,并且不会导致浏览器进行额外的分配(除非您使用 WWW.LoadFromCacheOrDownload 进行资源包缓存;此情况下将使用内存映射式虚拟文件系统,由浏览器的 IndexedDB 提供支持)。
When you see an error related to memory in a Unity WebGL build, it is important to understand whether it is the browser which is failing to allocate memory or if the Unity WebGL runtime is failing to allocate a free block of memory within the pre-allocated block of the Unity heap. If the browser is failing to allocate memory, then it may help to try to reduce the size used by one or more of the memory areas above (for instance by reducing the size of the Unity heap). On the other hand, if the Unity runtime is failing to allocate a block inside the Unity heap, you may want to increase the size of that instead.
Unity will try to interpret error messages to tell which of the two it is (and provide suggestions on what to do). Since different browsers may report different messages, that is not always easy, however, and we may not be interpreting all of them. When you see a generic “Out of memory” error from the browser, it is likely to be an issue of the browser running out of memory (where you might want to use a smaller Unity heap). Also, you may sometimes see browsers simply crashing when loading Unity content without showing a human-parseable error message. This can have many reasons, but is frequently caused by JavaScript engines requiring too much memory to parse and optimize the generated code.
Your server can emit the Large-Allocation http header for your content. This tells supported browsers (currently only Firefox) about your memory needs, allowing them to spawn a new process with an unfragmented memory space, or to perform other housekeeping to make sure that the large allocation succeeds. This can solve issues where the browser runs out of memory when trying to allocate the Unity heap, especially on 32-bit browsers.
When you allocate managed objects in Unity, they will need to be garbage collected when they are no longer used. See our documentation on automatic memory management for more information. In WebGL, this is the same. Managed, garbage collected memory is allocated inside the Unity heap.
One distinction in WebGL, however, concerns the points in time when garbage collection (GC) can take place. To perform garbage collection, the GC would normally need to pause all running threads and inspect their stacks and registers for loaded object references. This is not currently possible in JavaScript. For this reason, the GC will only run in WebGL in situations where the stack is known to be empty (which is currently once after every frame). This is not a problem for most content which deals with managed memory conservatively and has relatively few GC allocations within each frame (you can debug this using the Unity profiler).
However, the following code would fail running on WebGL, becuase it would not get a chance to run the GC between iterations of the loop, to free up memory used by all the intermediate string objects - which would eventually cause it to run out of memory in the Unity heap.
string hugeString = "";
for (int i = 0; i < 100000; i++)
{
hugeString += "foo";
}
2018–08–23 Page amended with no editorial review