【Unity】开发WebGL内存概念详解和遇到的问题

article/2025/7/1 9:27:43
自加入unity WebGL平台以来,Unity的开发团队就一直致力于优化WebGL的内存消耗。我们已经在Unity使用手册上有对于WebGL内存管理的详尽分析,甚至在Unite Europe 2015与Unite Boston 2015两届大会上,也有专题对其进行深入的讲解。然而,这方面的内容依旧是用户讨论的热门话题,因此我们意识到应当分享更多。希望本文能回答一些被频繁咨询的问题。
 
Unity WebGL与其它平台有何不同?
一些用户已经熟悉了部分内存有所限制的的平台。而对于其它如桌面和WebPlayer平台,到目前为止内存还不是问题。

在内存方面,主机平台相对其它平台较为简单,因为您可以准确的知道内存是如何使用的。这允许您可以很好的管理内存,并保证您的游戏内容完美运行。在移动平台,内存管理变的有些复杂,因为设备种类繁多,但至少您可以选择最低标准的设备,并根据市场情况忽视那些相较于该标准更为的低端设备。

在网页平台,就没有那么轻松了。理想情况下,所有终端用户都拥有64位浏览器和大量内存,但事实却相距甚远。首先,您无法通过任何方法知道,正运行您的内容的硬件规格。其次,除了用户的操作系统和浏览器外,您并不知道其它信息。最后,终端用户可能像运行其它网页一样运行您的WebGL内容。因此这是一个非常复杂的问题。

概览

下图是在浏览器上运行Unity WebGL内容时的内存概览:



上图展示了Unity 堆,Unity WebGL内容将需要向浏览器请求额外分配的内存。这是理解WebGL内存管理的重点,从而让您优化项目得以将用户流失率降至最低。

正如上图所示,存在几组内存分配:DOM,Unity堆,资源数据和代码,这些内容都会在网页加载时持久存在于内存中。而其它诸如 Asset Bundles, WebAudio 和 Memory FS 何时加载则取决于您的内容运行情况。(例如:Asset Bundle下载,音频播放等等)

在加载期间, 一些浏览器在asm.js解析和编译时会产生临时内存分配,这偶尔也会导致部分使用32位浏览器的用户出现内存溢出的问题。

Unity堆

通常来说,Unity堆是指包含了所有Unity特有的游戏对象、组件、纹理、着色器 等等的内存块。

在WebGL平台,Unity堆的大小需要提前获知,浏览器才能对此分配空间,并且内存空间一旦分配,就无法改变内存缓冲区大小。

负责Unity 堆内存分配的代码如下:

1.buffer = new ARrayBuffer(TOTAL_MEMORY);

这段代码可以在所生成的build.js中找到,并通过浏览器的JS虚拟机来执行。

TOTAL_MEMORY 是在Player Settings 中的WebGL Memory Size中设置的总内存。默认为256MB,但这是我们随意设定的值,事实上,一个空项目运行仅需16MB。

然而,真实世界中游戏内容可能会需要更多的内存空间,大部分情况下都需要256或者386MB。请记住,项目需要的内存越多,能够运行它的终端用户就越少。

源代码/编译代码内存

在代码可以被执行之前,它需要如下步骤:
  • 下载
  • 复制到一个文本域
  • 编译

请慎重考虑,上述的每一个步骤都将请求大量内存。因为:

  • 下载缓冲区是临时的,但是源代码和编译代码将持久存在于内存中。
  • 下载缓冲区和源代码大小,都是Unity所生成的未压缩的js大小。按照以下步骤,您可以估算它们需要多少内存:
    • 构建一个发布版本。
    • 将jsgz 、datagz重命名为*.gz文件,并通过压缩工具对它们进行解包。
    • 解压缩后的大小就是它们在浏览器内存中的大小。
  • 编译代码的大小取决于浏览器。

优化内存的一个简单方法是启用Strip Engine Code,这样您发布的版本将不包含那些不必需的原生引擎代码(例如:如果不需要2D物理模块,它将被剥离)。请注意:托管代码一定会被剥离。

千万要记住,异常捕捉和第三方插件也将增加代码大小。正如之前所说,我们已经注意到用户需要添加空值检查和数组边界检测的代码,但不希望完整的异常检测支持会带来过多的内存(及性能)消耗。要实现这点,您可以通过编辑器脚本传递 
–emit-null-checks 和 –enable-array-bounds-check 到il2cpp,例如

PlayerSettings.SetPropertyString("additionalIl2CppArgs", "--emit-null-checks --enable-array-bounds-check");

最后请记住,构建开发版本产生的代码尺寸更大,因为它不曾缩减。这不是问题,毕竟最终交给用户的会是发布版。

资源数据

在其它平台上,一个应用可以简单地访问位于固定存储空间(硬盘,闪存等等)的文件。而在网页平台上这是不可能的,因为出于安全考虑,网页平台无法访问真正的文件系统。因此,Unity WebGL 数据(.data文件)一旦被下载,就会永远存储在内存中。这样做的缺点就是它相对其它平台将需要更多的内存(例如5.3中.data文件以lz4压缩的形式存储在内存中)。例如,下图是分析器显示的一个项目生成了约40MB的数据文件(在256MB Unity堆的设置下):



.data 文件中包含了什么?它是Unity所生成的文件集合,包含以下内容:data.unity3d (所有的场景,它们依赖于Resources文件夹中的资源和所有内容),unity_default_resources和少量引擎所需的小文件。

为了知晓资源的准确总大小,您需要在发布至WebGL平台后查看Temp\StagingArea\Data目录下的data.unity3d (Temp文件夹将会在Unity编辑器关闭时被删除)。另外,您也可以通过查看UnityLoader.js 中的DataRequest差值得知素材资源的准确大小。

new DataRequest(0, 39065934, 0, 0).open('GET', '/data.unity3d');
(这段代码根据Unity版本不同,写法可能有些区别——示例是Unity 5.4)

内存文件系统
虽然不存在真实的文件系统,正如前文所述,您的Unity WebGL内容仍然可以读写文件。相对于其它平台的主要区别在于,WebGL平台的文件输入/输出操作实际上都是对内存的读/写操作。很重要一点是,这个内存文件系统并不存在于Unity 堆中。因此,它将需要额外的内存。例如,下面这个输出数组到文件的示例:

var buffer = new byte [10*1014*1024];

File.WriteAllBytes(Application.temporaryCachePath + "/buffer.bytes", buffer);

这个文件将会被写入到内存中,并且在浏览器的分析器也可以查看到。


请注意:Unity堆的大小为256MB。

同样,Unity的缓存系统依赖于文件系统,所以WebGL平台整个缓存存储也是在内存中进行的。这意味着像PlayerPrefs和缓存的Asset Bundles也会被持久化到内存中,而不存在于Unity堆中。

Asset Bundles

减少WebGL平台内存消耗的最佳方法之一是使用Asset Bundles (如果您对Asset Bundles不熟悉,请查阅Unity使用手册或通过教程学习)。然而,根据使用方式不同,它们将会对内存消耗带来巨大影响(Unity堆中和堆外都会受此影响),这将有可能导致您的内容无法运行在32位浏览器上。

如果真的需要使用Asset Bundle,您会将所有资源打包到一个单独的Asset Bundle吗?

千万别这么做!即使那样可能会减少网页加载期间的压力,您仍然需要下载(极可能无比巨大的)Asset Bundle,从而导致内存使用高峰。来看看下载AB前的内存使用情况。


如您所见,256MB被分配给Unity堆。下图是没有经过缓存的Asset Bundle下载:


现在看到的是额外的缓存,大约与硬盘中的Asset Bundle(约65mb)大小相同,它是通过XHR分配的。这只是一个临时缓存,但它将导致连续几帧的内存高峰,直至垃圾收集器启动。

如何最小化内存高峰?为每个资源创建一个Asset Bundle?想法不错,但明显不合实际。

事实上,对于如何做能够减少内存高峰并没有普遍的标准,这取决于您项目的实际需求。

最后,在资源使用完毕后记得通过AssetBundle.Unload卸载Asset Bundle。

Asset Bundle缓存

Asset Bundle缓存与其它平台一样,您只需要使用WWW.LoadFromCacheOrDownload。它们最大的区别就是内存消耗。在Unity WebGL中,AB缓存依赖于IndexedDB,IndexedDB是由目前内存文件系统所支持的emscripten编译器实现。

下图使用LoadFromCacheOrDownload下载Asset Bundle的内存使用情况:


如您所见,Unity堆使用了512MB,并额外分配了约4MB的内存。

下图是加载Asset Bundle后的内存情况:


额外需要的内存跳到了约167mb。这是该Asset Bundle所需的额外内存(压缩包约为64mb)。下图是js虚拟机垃圾收集器启动后的内存情况:


可以看到现在有了一些改善,但仍需约85mb的内存,其中大部分内存用于将Asset Bundle缓存到内存文件系统。这些内存即使卸载了Asset Bundle也不会回收。还有一点很重要,当玩家第二次在浏览器中运行游戏时,这些内存会被立即加载,甚至在加载Asset Bundle之前。

下图是Chrome的内存截图以供参考:


同样,在Unity堆外还有其它缓存相关的临时内存分配,以供Asset Bundle系统使用。坏消息是最近我们发现它比预想的更大。好消息是它将在未来的Unity 5.5 Beta 4,5.3.6 Patch 6和5.4.1 Patch 2中得以修复。

对于更早的Unity版本,万一您的Unity WebGL内容已经上线或即将发布,而您又不想升级项目,一个快速的变通方法是通过编辑器脚本的设置以下属性:

PlayerSettings.SetPropertyString("emscriptenArgs", " -s MEMFS_APPEND_TO_TYPED_ARRAYS=1", BuildTargetGroup.WebGL);

最小化Asset Bundle缓存内存消耗的长远解决方案是,使用WWW构造器替代LoadFromCacheOrDownload(),或者您使用新的UnityWebRequest API 时,调用UnityWebRequest.GetAssetBundle()不要带有哈希或版本参数。

其次是在XMLHttpRequest层使用替代的缓存机制,绕过内存文件系统,将下载的文件直接存储到indexedDB中。我们已经开发了这样的工具并发布在Asset Store中。您可以免费将它用于您的项目,也可以自定义以满足特殊需求。

Asset Bundle压缩

Unity 5.3和5.4均支持LZMA和LZ4两种压缩方式。然而,即使使用LZMA(默认)压缩相对于LZ4或未压缩下载的包更小,但它在WebGL平台上还是有些缺点:它会导致明显的运行延迟,并且需要更多的内存。因此强烈建议使用LZ4或者未压缩的格式(实际上,Unity 5.5的WebGL平台将不再支持对Asset Bundle的LZMA压缩),为了弥补相比LZMA压缩的下载尺寸过大,您可能希望使用gzip/brotli来压缩Asset Bundle,并配置到您的服务端。

查阅Unity使用手册以获得更多关于Asset Bundle压缩的信息

网页音频

音频在Unity WebGL上的实现方式有所不同。这对内存意味着什么?

Unity将会在JavaScript中创建特定的AudioBuffer的对象,以便它们可以通过WebAudio进行播放。

由于WebAudio缓存位于Unity堆外,因此无法通过Unity 分析器进行跟踪分析,您需要使用浏览器专用的工具,来查看音频使用了多少内存。示例如下(火狐浏览器, about:memory page):


考虑到那些Audio Buffers保存的是未解压的数据,其可能不适用于大型音频片段资源(例如:背景音乐)。对于那些资源,你可能希望自己编写js插件,以便使用<audio>标签。这种方式下音频文件会保持压缩,因此需要的内存更少。

FAQ

问:减少内存使用的最佳实践是什么?
答:概括如下:
  • 减少Unity堆的大小
  • 尽可能保持“WebGL Memory Size”足够小
  • 减少代码量
  • 启用Strip Engine Code
  • 禁用异常检测
  • 避免使用第三方插件
  • 减少数据大小
  • 使用Asset Bundles
  • 使用Crunch纹理压缩

问:是否存在能够决定最小WebGL Memory Size的策略?

答:有,最佳策略是使用内存分析器,分析您的内容实际所需的内存大小,然后据此改变WebGL Memory Size。

以空项目为例,内存分析器告诉我们总的使用量仅为16mb(这个值可能在不同Unity版本上有所不同):这意味着只须设置WebGL Memory Size大于16MB即可。当然,内存的总使用量将会依据您的内容而有所不同。

然而,如果因为某些原因无法使用分析器,可以简单地通过不断地减少WebGL Memory Size 值,直到发现您的内容真正所需要的最小内存使用量为止。

另外非常值得注意的是,任何不是16的倍数的值都将被自动的四舍五入(在运行时)为下一个16的倍数,这是Emscripten编译器所要求的。

WebGL Memory Size(MB)设置将决定生成的html中TOTAL_MEMORY(bytes)的值。


所以,为了在不重新构建项目的前提下,反复测试内存堆的值,推荐使用更改html的方式。一旦您通过此方式发现适合的值,只需在Unity项目设置中更改WebGL Memory Size即可。

最后,记住Unity的分析器将占用一些来自Unity堆的内存,所以在使用分析器时可能需要增加WebGL内存大小。

问:运行时发生内存溢出,如何修复?

答:这取决于是Unity,还是浏览器的内存溢出。这个错误信息将会指出问题所在以及解决办法:“如果您是该内容开发者,请在WebGL设置中为您的应用分配更多(或更少)的内存。”此时您可以据此调整WebGL内存大小设置。然而还有很多可以解决内存溢出的方法。如果出现以下错误信息:


除了消息内容,您还可以尝试减少代码和数据的大小。这是因为当浏览器加载网页时,它将试图为一些内容寻找空余的内存,其中最重要的是:代码,数据,Unity堆和被编译的asm.js。它们可能相当大,尤其是数据和Unity堆内存,这对32位浏览器来说可能是问题。

在一些例子中,尽管存在足够多的空余内存,浏览器仍将加载失败,因为内存是碎片化的。这就是为什么有时候您的内容可能在重启浏览器之后,可以成功加载的原因。

另一种情况是,当Unity 内存溢出时提示以下信息:


在这种情况下,您需要优化您的Unity项目。

问:如何衡量内存消耗?

答:为了分析内容所使用的浏览器内存,可以使用火狐浏览器的内存工具或Chrome堆快照。但它们不会显示WebAudio内存使用情况,因此还可以获取火狐浏览器的about:memory页面快照,然后通过搜索“webaudio”找到。如果您需要通过JavaScript分析内存,请尝试使用window.performance.memory(只支持Chrome)。

使用Unity分析器测量Unity堆内存使用。但请注意,您可能需要增加WebGL的内存大小,以便能够使用分析器。

此外,我们一直在致力于开发一个新的工具,以便您能分析发布版本:构建WebGL版本,然后访问 http://files.unity3d.com/build-report/ 即可使用该工具。虽然这在Unity5.4下已经可用,但请您注意,这还是正在开发中的功能,并且随时会更改或被删除。但至少现在可以使用它达到测试的目的。

问:WebGL Memory Size的最小值与最大值是多少?

答:16MB是最小的,最大是2032MB,然而我们通常建议保持在512MB以下。

是否可能出于开发目的而需要分配超过2032MB的内存?

这是一个技术上的限制:2048MB(或更多)将会超出TypeArray所用的32位有符号整型的最大值,而TypeArray被用于在JavaScript中实现Unity堆。

问:为何Unity 堆大小不可改变?

答:我们一直在考虑使用Emscripten编译器标志ALLOW_MEMO

http://chatgpt.dhexx.cn/article/7naLpbiF.shtml

相关文章

Unity WebGL项目打包后本地打开报错问题解决方法

在Unity打包WebGL项目后&#xff0c;本地打开html页面出现错误提示。 Failed to download file Build/Unity Web.data.gz. Loading web pages via a file:// URL without a web server is not supported by this browser. 在网上试了好几种方法&#xff0c;综合起来终于跑起来…

Unity WebGL错误集锦

ips: 0 Unity的PlayerSettings的otherSettings或者Publish Settings里面的Enable Exceptions里面选择Full StackTrace &#xff0c;可以在打出的包中的浏览器的webgl打印出错误调用栈&#xff0c;具体在哪个Setting取决于unity的版本 1 一般出现了错误 可以看看在浏览器里面传的…

Unity在网页上运行WebGL问题

Unity在网页上运行WebGL问题&#xff1a;It seems your browser does not support running Unity WebGL content from file:// urls. Please upload it to an http server, or try a different browser 前言Unity打包WebGL在网页上运行遇到的问题解决方法 前言 项目要开发B/S模…

Unity 之 发布 WebGl 遇到的问题

最近发布的WebGL的时候遇到了些问题&#xff0c;上网查了一下&#xff0c;说法不一&#xff0c;又说和发布目录有关的&#xff08;这个我试了发布到桌面上也是可以的&#xff0c;建议发布在和项目同级目录下&#xff09;&#xff0c;也有说需要下载Unity 的补丁的&#xff08;我…

Unity打包WebGL的全过程及在打包和使用过程中会遇到的问题

目录 概要 Unity打包WebGL PlayerSettings设置 Resolution and Presentation Other Settings Publishing Settings 本地服务器测试环境配置 问题盘点 概要 盘点Unity在Build WebGL环境包时需要的配置以及遇到的难题 Unity打包WebGL PlayerSettings设置 Resolution and…

Unity WebGL 遇到的各种问题

内容持续更新&#xff01;如果你有遇到过奇怪的问题&#xff0c;也可以在评论局反馈。 1.Unity 发布WebGL1.0版本&#xff0c;需要把程序包控制在100M以内。 提示1&#xff1a;RangeError: Start offset undefined is outside the bounds of the buffer&#xff08;开始偏移量…

【unity发布webgl】遇到的问题和解决办法

1.发布部署出来的链接放到手机上测试。 ios&#xff1a;20秒读条然后闪退&#xff1b;vivo:9秒读条闪退&#xff1b;小米&#xff1a;15秒进入 然鹅&#xff0c;资料只有130kb的图片。 2.报这个警告&#xff0c;修改Build文件夹里的UnityLoader.js取消 移动端展示弹出提示框&a…

【Unity】打包WebGL项目遇到的问题及解决记录

目录 Unity中打包时注意事项切换平台为WebGL平台设置Player Setting分辨率预设其它设置发布设置确保项目路径没有中文 选择Assets同级目录打包 打包后将项目部署到IIS上发布安装IIS并添加网站为服务器添加 MIME Type 映射和跨域访问权限添加MIME Type映射添加跨域访问权限 尝试…

发布WebGL遇到的问题

本文地址&#xff1a;https://blog.csdn.net/t163361/article/details/129734803 最近准备申请新星创作者&#xff0c;需要2000个粉丝关注&#xff0c;觉得文章有用的&#xff0c;请点一下左侧边栏的关注&#xff0c;谢谢。 以下为发布WebGL时遇到的问题以及解决方案。相同的问…

关于Webgl实际中遇到的一些坑,与大家分享。

我们在webgl的开发中&#xff0c;可能会遇到这样或者那样的问题&#xff0c;在这里与大家分享。 首先&#xff0c;我们要懂得如何找到问题。 打开Firefox火狐浏览器的web控制台。 这样我们更可以知道哪一个环节出了问题&#xff0c;就可以对症下药&#xff0c;解决问题。节省…

个性化推荐系统 - 01简述

前言 在互联网发展的早期,内容比较匮乏,不论在资讯,电商,还是广告行业。那个阶段诞生了搜索引擎。解决了信息查找的问题。随着互联网迅速发展起来,互联网上面的内容几何式增长。用户获取信息的途径不再困难。怎么样在海量的信息中找到用户感兴趣的内容,就是我们现在要解决的问…

4个方面,系统总结个性化推荐系统

作者&#xff1a;Placeless 全文共 7393 字 13 图&#xff0c;阅读需要 16 分钟 ———— / BEGIN / ———— 现在的人们面对信息过载问题日益严重&#xff0c;好的个性化推荐将能够很好的提升用户体验&#xff0c;提高用户使用产品完成任务的效率&#xff0c;更好的留住用户&…

推荐系统基础(2):个性化推荐系统简述

1.推荐系统含义、目标 推荐系统根据用户的历史、社交、上下文环境等信息去判断用户当前感兴趣的内容。 推荐系统的业务&#xff1a; 物料组装&#xff1a;生产广告&#xff0c;实现文案、图片等内容的个性化物料召回&#xff1a;在大量内容中召回一个子集作为推荐的内容物料…

CSDN个性化推荐系统-负反馈测试

文章目录 前言一、uc不感兴趣标签过滤测试1.uc不感兴趣标签获取(uc_unlike_tag_list)1.1个人中心界面1.2从标签中可以发现什么&#xff1f;1.3与研发确认点1.4设计开发1.5接口获取结果 2.推荐流文章标签获取(tag_list)2.1部分代码2.2基本标签校验2.3基本标签校验结果 3.推荐流u…

个性化推荐系统设计(2.1)——推荐算法介绍

协同过滤算法 协同过滤(Collaborative filtering, CF)算法是目前个性化推荐系统比较流行的算法之一。 协同算法分为两个基本算法&#xff1a;基于用户的协同过滤&#xff08;UserCF&#xff09;和基于项目的协同过滤&#xff08;ItemCF&#xff09;。 基于属性的推荐算法 基于…

[推荐系统]基于个性化推荐系统研究与实现(1)

目 录 一、搜索引擎与推荐系统 二、推荐系统原理与算法 2.1 Jaccard系数 2.2 余弦相似度 三、数据定向爬取及电影数据集 3.1 爬取近七日天气预报数据存入DB数据库&#xff0c;分为五步完成。 3.2 爬取豆瓣电影数据集存入CSV文件&#xff0c;分四步。 3.3 电影&#xf…

如何支持研发对CSDN个性化推荐系统重构

目录 大地图工具构建数据治理保持发布重视测试小结引用 一个以内容服务为主的软件&#xff0c;它的推荐系统在数据侧对软件产生着举足轻重的作用。数据的三个方面决定了这个内容软件的档次。 数据的质量好坏数据和用户需求的相关性好坏数据的层次体系好坏 通常&#xff0c;我…

智能个性化推荐系统设计

推荐系统构成 * 召回层 - 对海量的数据进行召回 * 排序层 - 对召回后的数据进行排序&#xff0c;排序结果返回给用户 推荐系统架构 基于物品的推荐系统架构 基于用户的推荐系统架构

个性化推荐系统设计(4.1)——案例分析

在过去的十年中&#xff0c;神经网络已经取得了巨大的飞跃。如今&#xff0c;神经网络已经得以广泛应用&#xff0c;并逐渐取代传统的机器学习方法。 接下来&#xff0c;我要介绍一下YouTube如何使用深度学习方法来做个性化推荐。 由于体量庞大、动态库和各种观察不到的外部因素…

141.如何个性化推荐系统设计-1

141.1 什么是个性化推荐系统&#xff1f; 个性化推荐系统就是根据用户的历史&#xff0c;社交关系&#xff0c;兴趣点&#xff0c;上下文环境等信息去判断用户当前需要或潜在感兴趣的内容的一类应用。大数据时代&#xff0c;我们的生活的方方面面都出现了信息过载的问题&#…