Unity加载优化-将基于LZMA的ab压缩方案修改为LZ4压缩的过程

# 观前提示

本文适合需要了解LZMA以及LZ4进行ab打包方案同学,以及会将一些资源管理的方案。如果恰好你也用xlua-framework,那就更适合你了。

# 优化起因

最近新游戏在Iphone6上出现了闪退的情况,出现闪退是在加载完成后的一瞬间,用脚指头想都知道内存超过上限了,查了下Iphone6内存为1GB,可使用最大内存上限为645M,理应是一个很高的内存值,由于新游戏是一款3d大地图城建游戏,模型建筑特别多,内存也出现溢出。这让我不得不想办法优化内存以及加载速度,一般的内存优化是从纹理,模型,动画这些资源下手,但是还没等我查到这一步,就发现之前使用的框架存在很大的内存优化空间,所以就有了这一篇优化文章。

# 优化前的情况

之前使用的框架叫xlua-framework,框架逻辑还是挺清晰的,可以再次基础上修改出比较好的框架。这次也是基于此进行优化,虽然是基于框架进行优化,但是主要还是将LZMA到LZ4压缩方案需要注意及修改的地方。

老框架使用LZMA压缩方式,游戏加载的时候先通过UnityWebRequestAssetBundle接口对AssetBundle进行全量加载,加载后缓存AB包,然后再将AB包内所有资源进行缓存。这个时候AB包,以及AB包内所有资源都在内存中。另外通过引用计数去维护资源,在切换场景的时候尝试卸载没有使用的资源,如果一个AB没有一个资源被使用就把这个AB卸载。

### 问题

1.LZMA采用流压缩方式(stream-based),压缩率会比LZ4更高,体现在包体更小,但是问题也很严重。LZMA只支持顺序读取,所以加载AB包时,需要将整个包解压,会造成卡顿和额外内存占用。这也是为什么在有些复杂UI上首次打开会造成卡顿。

2.加载AB包后将所有资源进行了缓存,导致了如果AB包资源利用率在短时间利于率不高的时候,造成了很高的内存浪费。

3.一套引用计数规则非常复杂,当资源过多的时候建立引用关系都是很费时的,其中的常驻包的设置逻辑也是非常具有不确定性。

### 基于LZ4优化方案对应解决了什么

1.LZ4采用块压缩方式(chunk-based),块压缩的数据被分为大小相同的块,被分别压缩,虽然压缩率不及LZMA,但是读取效率比LZMA高非常多

2.LZ4压缩的AB包,使用LoadFromFile()或LoadFromStream()只会加载AB包的Header,相比于直接加载解压整块AB包,效率更进一步提高。另外一个很重要的点,由于可以只加载Header,因此AB包可以做到一旦加载到内存后就再不卸载,此时只需要管理从AB包中读取出来的资源的生命周期。

3.对于之前使用引用计数的优化,由于Unity原本资源管理就是使用引用计数去维护,这里再建立一套内部的引用计数,不仅多余而且很浪费CPU资源,而且效果不一定很好。这个时候我们可以建立一套弱引用管理体系,通过弱引用去持有资源,在触发Resource.UnloadUnusedAssets()再去清除弱引用失效的对象。

下面这张图是AB资源及其内部资源的生命周期

扫描二维码关注公众号,回复: 15436008 查看本文章

# 优化步骤

## 将LZMA的压缩方式修改为LZ4

将打包参数添加BuildAssetBundleOptions.ChunkBasedCompression即可

BuildAssetBundleOptions buildOption = BuildAssetBundleOptions.IgnoreTypeTreeChanges | BuildAssetBundleOptions.DeterministicAssetBundle | BuildAssetBundleOptions.ChunkBasedCompression;

## 修改AB包读取方式

//读取ab包
AssetBundle.LoadFromFileAsync(url)

这里需要注意一点的就是,LoadFromFileAsync与WWW或UnityWebRequest区别在于是在windows下不需要file://前缀

下面这个回答来源:Unity 5.3 bundles - "Unable to open archive file" - Unity Forum

 

 ## 修改引用计数为弱引用管理

在一个资源被加载好后,为其建立一个WeakReference对象,如果下次还要加载相同资源,直接返回这个弱引用的Target对象。在卸载场景后,将失效的弱引用清除即可。

### 为什么能用弱引用去管理资源??

首先我们要明白当我们调用Resource.UnloadUnusedAssets,这个UnusedAssets代表什么意思??我们先了解被使用(used),不是被使用的就是Unused的了。下面是被视为使用的情况:

1)静态变量引用的资源,Monobehavior中变量/属性引用的资源被视为

2)被C#层强引用或被Lua简介引用的资源

因此,如果我们使用弱引用去管理资源,使用UnloadUnusedAssets就可以直接卸载我们不使用的资源,这样就无须再做一次引用计数的管理。

来源:UnloadUnusedAssets - what exactly does "unused" mean - Unity Answers

# 最后

 最后通过上面这张图了解下整个流程,我们先通过LZ4压缩了ab包,因此可以使用LoadFromFile只加载AB的Header到内存,在后续加载资源的时候,从AB缓存中读取资源,并用弱引用持有引用,方便后续再加载的时候,直接返回资源,就不去AB包中读取。在场景卸载后,先销毁游戏对象,然后调用Resources.UnloadUnusedAsset,弱引用中的Target失效,这时候清理弱引用。如此循环加载速度保证了,内存也维持到了一个比较好的水平。下一步需要优化就需要对于具体资源以及代码逻辑进行优化,就没有那么容易提升那么多效果了。下面放一张加载测试图,另外具体的修改代码后续会提交到git上,之后再补上。

------------------------------------- 分割线-------------------------------------

git资源已上传,可以拉下来看看具体修改xlua-frame work

猜你喜欢

转载自blog.csdn.net/weixin_36719607/article/details/121257948
LZ4