AssetBundle最新内存加载细节

来源:互联网 发布:iphone小说软件 编辑:程序博客网 时间:2024/05/22 06:45

目的

随着Unity版本的不断升级,Assetbundle 的功能也在不断的完善中, 过去很多团队将每个资源文件打成一个ab的做法, 在Unity5.4版本开始,似乎已经变得越来越没有必要, 本文得益于官方的一篇ab的文章https://unity3d.com/es/learn/tutorials/topics/best-practices/assetbundle-fundamentals#Loading_Asset_Bundles , 意在通过讲解ab加载到内存的损耗细节,来说明Unity5.4版本后的ab将让热更新管理方案变得比之前更简洁和便利。

最疑惑的问题

在学习和了解ab的过程中, 最让我疑惑的地方是下面这张图:
这里写图片描述
问题在于,对于加载到内存的assetbundle, 上图中的紫色部分描述的内存镜像是否包含整个assetbundle内容? 为此首先在网上搜了大量的博文,可并没有让人满意的答案, 针对这块细节, 要么说的很含糊, 要么咬定assetbundle内存镜像确实会完全拷贝一份ab到内存中。 为了确认结果,我便亲自用UnityEditor试了一下, 加载一个1Mb大小的ab后,内存确实多消耗了1MB, 到此, 似乎结论已经清晰了(其实不然), 可Unity这种粗放的做法还是让我很难相信这是真的。 于此同时, 项目组为了解决加载ab导致额外内存消耗的问题, 在一个项目中决定采用将每个资源文件打成一个ab的做法, 当然UI图片资源也被打成静态图集, 将一个图集打成一个ab,这样在程序动态运行时,每加载一个资源,便缓存这个资源并释放对应的ab,这样的做法,确实能够避免加载ab造成的内存资源浪费的问题。
事情到此虽然有了ab加载的解决方案,但是还是有个疑问让我很困扰:unity为何要将ab的资源完全镜像一份在内存中,而实际从ab中loadasset的资源又会在内存中创建一份(即上图中绿色部分),这么浪费内存的事情难道不能用更有效的方式解决么?比如ab创建的内存镜像只存储实际资源的索引关系,通过索引可以直接向系统硬盘加载需要的资源。

随着时间的推移和Unity技术的成熟, 转眼来到2016年, Unity也发展到了5.5版本, UGUI在项目中的使用者越来越多,UGUI中的Sprite Picker(动态图集)功能更是可以让广大开发者在做界面时不用去关心图集的概念,提升开发效率。 然而这项技术在实际应用中如需要和资源热更新结合使用的话,就会产生一个问题:由于动态图集不需要开发者事先将Ui图片打包成图集,在做资源热更新时,如果使用一个文件打包成一个ab的资源打包方式,这就意味着,原本Ngui中的一张图集, 在Ugui中,会变成N张小图片,每个图片就是一个ab, 这会大大增加ab的数量,影响加载效率。这个问题又勾起我对ab一直挥之不去的那个疑问, Unity发展至此, 难道还是把整个ab加载到内存镜像吗?然后在看了文章开头的那篇官方文章后, 我的顾虑终于消除了,让我们来看看官方的说法。

根据官方的文章:
*Prior to Unity 5.3, Objects could not be compressed individually inside an AssetBundle. As a consequence, if a version of Unity before 5.3 is instructed to read one or more Objects from a compressed AssetBundle, Unity had to decompress the entire AssetBundle. Generally, Unity cached a decompressed copy of the AssetBundle to improve loading performance for subsequent loading requests on the same AssetBundle.
Unity 5.3 added a LZ4 compression option. AssetBundles built with the LZ4 compression option will compress individual Objects within the AssetBundle, allowing Unity to store compressed AssetBundles on disk. This also allows Unity to decompress individual Objects without needing to decompress the entire AssetBundle.*

上文是说在5.3之前的版本中,ab是作为一个整体进行lzma压缩的,所以即使你只是需要用到ab中的一部分资源,压缩后的ab还是需要通过整体解压缩后才能使用。所以通常unity会缓存一份ab的解压用于提高资源加载时的效率。 然后在5.3版本中,untiy支持了LZ4压缩,这种压缩支持将ab中的个体资源分开压缩,这样就可以在ab加载资源时分别对资源进行解压而非整体解压。
那么分开解压对我们的热更新策略会有什么好处呢?继续往下看官方的说法:

3.4.2. AssetBundle.LoadFromFileUnity 5.3 update: This API was renamed in Unity 5.3. In Unity 5.2 (or older), this API was known as AssetBundle.CreateFromFile. Its functionality has not been changed.
AssetBundle.LoadFromFile is a highly-efficient API intended for loading uncompressed AssetBundle from local storage, such as a hard disk or an SD card. If the AssetBundles are uncompressed or LZ4 compressed, the API will behave as follows:
Mobile devices: The API will only load the AssetBundle’s header, and will leave the remaining data on disk. The AssetBundle’s Objects will be loaded on-demand as loading methods (e.g. AssetBundle.Load) are called or as their InstanceIDs are dereferenced. No excess memory will be consumed in this scenario.
Unity Editor: The API will load the entire AssetBundle into memory, as if the bytes were read off disk andAssetBundle.LoadFromMemoryAsync was used. This API can cause memory spikes to appear during AssetBundle loading if the project is profiled in the Unity Editor. This should not affect performance on-device and these spikes should be re-tested on-device before taking remedial action.
Note: On Android devices with Unity 5.3 or older, this API will fail when trying to load AssetBundles from the Streaming Assets path. This is because the contents of that path will reside inside a compressed .jar file. For more details, see the section Distribution - shipped with project section of the AssetBundle usage patterns chapter. This issue is resolved in Unity 5.4. Games built with Unity 5.4 or newer can now use this API to load Asset Bundles from Streaming Assets.
Note: Calls to AssetBundle.LoadFromFile will always fail for LZMA-compressed AssetBundles.

也就是说 AssetBundle.LoadFromFile这个高效的api会从本地硬盘中加载未压缩或者采用LZ4压缩的ab, 且在移动端,Api只会加载ab的header, 而不会把整个ab加载进内存, 在进行诸如AssetBundle.Load时才会根据头文件的信息,将硬盘中的ab对应数据加载出来, 而针对老版本的LZMA压缩的数据,LoadFromFile会永远返回失败, 真相水落石出,Unity5.4版本开始, 将多个资源打在一个ab里将成为一个新的选择,你将不用再担心加载ab且不立即释放ab所造成的额外内存开销, 因为在移动端ab在内存中的镜像只是ab的header部分

对于我们项目来讲, 使用的原因很简单, 因为项目计划使用UGUI的自动图集功能, 而自动图集功能需要将多张图片同时打包到一个ab中才能正常生成图集(针对这点, 我会另外写一篇文章解释), 这就需要项目能够有加载涵盖多个资源的ab且不即可释放, 各位基友可根据项目的情况自己决定.另外另一篇官方的文章中也提到:

AssetBundles themselves must be carefully managed. An AssetBundle backed by a file on local storage (either in the Unity cache or one loaded via AssetBundle.LoadFromFile) has minimal memory overhead, rarely more than 10-40 kilobytes. This overhead can still become problematic when a large number of AssetBundles are present.
虽然LoadFromFile加载的ab内存开销已经很小了, 几乎不会超过10-40kb ,但是还是要注意同时加载大量ab的情况.

好了,ab加载的内存消耗问题我想已经交代的很清楚了, 祝大家好运 ;)

1 0
原创粉丝点击