首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

AssetBundles如何影响运行时内存?看这一篇就够了

不论程序是借助内容分发网络(CDN)还是打包好的二进制文件,无一例外都会涉及AssetBundle(资源集合)的运用。AssetBundle文件内含一个或多个序列化的资源(纹理、网格、音频片段、着色器等等),用于在运行时的资源加载。

AssetBundle文件可以直接为系统所用,也可以经由如Unity Addressable Asset System(Unity可寻址资源系统,简称Addressable)来使用。可寻址系统是引擎的一个插件,可在项目中以更为直接、通用的方式管理项目中的资源,是基于AssetBundle的抽象层。Addressable可以减少开发者与AssetBundle的直接互动,因此理解AssetBundles对内存使用的影响对提升性能大有裨益。

我们推荐开发者在制作新项目时使用Addressable系统,而非直接使用AssetBundle。如果项目已经使用了AssetBundle,本篇将介绍AssetBundles如何影响运行时内存,协助改善内存使用。

内存缓存的内存占用

当Unity使用WWW类(已弃用)或UnityWebRequestAssetBundle(UWR)下载LZMA AssetBundle时,引擎会借助内存和磁盘缓存优化AssetBundle文件的取得(fetch)、再压缩(recompress)和版本控制(versioning)流程。

加载到内存中的AssetBundle会占用很多内存。除非你想频繁、快速地使用AssetBundle中的内容,不推荐将AssetBundle加载到内存中,使用磁盘缓存更为妥当。

如果在UnityWebRequestAssetBudnle的API中设定好了版本信息或对应哈希参数,Unity会将AssetBundle数据储存到磁盘上,如果没有则会将其存储到内存中。存储行为可经由UseAssetBundleCache字段控制。注意:可寻址系统默认会使用磁盘缓存。

AssetBundle.LoadFromFile和AssetBundle.LoadFromFileAsync只使用内存存储LZMA AssetBundle,因此推荐使用UnityWebRequestAssetBundle API来保存文件。如果该API不可用,也可以使用AssetBundle.RecompressAssetBundleAsync()将LZMA AssetBundle重新写入磁盘。

我们内部测试表明,使用磁盘和使用内存缓存时,RAM使用量相差了几乎一个量级。因此,请仔细衡量好应用内存、磁盘空间的使用和资源实例化的时间。

若想确定AssetBundle缓存对应用内存使用的影响有多大,可使用原生分析器(我们选择了Xcode的Allocaions Instrument)检查ArchiveStorageConverter类占用的内存。如果该类在RAM中占用了超过10MB,则AssetBundle可能被储存到了内存中。

Xcode的Allocations Instrument展示了ArchiveStorageConverter类的内存使用量

找出AssetBundle的实际加载内容

在大项目的AssetBundle中,Unity默认并不一定会减少文件的重复信息。而要识别AssetBundle文件中重复的数据,可以使用AssetBundle Analyzer。该工具由咨询开发部的一位同事用Python编写而成,使用命令行控制,可提取出AssetBundle中的信息,将其存储到一个SQLite数据库,用于审查。随后,你可以使用SQLite专用的DB Browser访问数据库,排查出构建管线中的不足之处,此方法适用手动或Addressable系统制作的资源集合文件(bundles)。

使用SQLite专用的DB Browser分析AssetBundle Analyzer传来的结果

或者,也可以下载使用AssetBundle Browser工具。请注意该工具的功能与Addressable类似,所以如果项目中使用的是可寻址系统,工具并不会有特别的作用。

AssetBundle Browser工具可以查看、编辑当前Unity项目中的AssetBundle文件配置,提供构建功能,还有一些非常好用的功能,比如它会告知用户依赖里重复调用了哪些资源(比如纹理)。

AssetBundle Browser工具会警告部分文件由于多个集合(Bundle)的依赖调用而被重复复制重复资源的内存占用

在制定资源的AssetBundle文件时,你需要注意资源的依赖。不管文件的组织方式如何,Unity只会区分应用程序中使用的资源(存于程序或资源文件夹中),和AssetBundles文件加载的资源。两种资源可看作放在了两个世界中,AssetBundle文件并不能强行引用资源文件夹中的实例。而要引用这些资源,Unity会将其拷贝放到AssetBundle中。

两个组件都引用了“logo”图片。在组件被序列化写入存档后(捆绑到玩家版本或AssetBundle文件中),每个组件都会使用图片的拷贝

以游戏的logo为例,在游戏启动时,logo会显示在UI中。由于加载屏幕必须在资源流传输到磁盘时显示,logo资源最好是包括在了程序中,便于立即使用。

同样的logo还用在了选择语言、音效等等设置的选项菜单中,如果该UI是经由AssetBundle加载的,则AssetBundle文件会自行创建logo资源的拷贝。

如果加载屏幕和选项菜单同时加载,两个logo资源的拷贝也会同时加载,造成重复使用。

该问题的解决方案是废弃屏幕引用的硬链接。如果logo存在于AssetBundle中,则在引用前需要将资源流传输到磁盘上。如果logo存在于程序中(比如在Resource文件夹下),则选项菜单UI需要用弱引用和Resources.Load这类API加载logo资源。

我们可用一串字符弱引用logo图片。脚本需要使用字符将图片指定到正确组件,在运行时加载图片

脚本需要使用字符来指定资源到正确组件,在运行时加载图片。当然也有一种折衷方案:将包含logo资源的AssetBundle包括在应用的StreamingAssets目录下。虽然仍要从AssetBundle加载资源,但由于Bundle存在了本地,不会产生额外的内容下载时间。

使用字符、路径或GUID引用资源并不直观易懂,因此你可以为弱引用的字段制作带引用拖拽功能的自定义检视器。别忘了用Unity的MemoryProfiler资源包找出那些内存中重复实例化的资源。可寻址系统中需要使用特定的方法来检查重复的引用(更多信息详见说明文件)。

结论

可寻址系统是基于AssetBundle的抽象层,而理解背后的工作原理后,就能更好地避免类似文中的性能耗费问题。

我们将在未来进一步讨论该系列话题。若有任何建议,请在评论区留言吧!

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20200414A0BK8200?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券