http://blog.csdn.net/lodypig/article/details/51879702

AssetBundle内存占用

  先上图,Don’t panic。 

  我们从AssetBundle中加载资源一般会经过三个步骤:

  1. www、LoadFromFile、LoadFromMemory等接口加载AssetBundle本身。
  2. 通过AssetBundle.LoadAsset()等接口从AssetBundle中加载资源。
  3. 对于GameObject类资源,还需通过GameObject.Instantiate()创建clone。

  这几个步骤,都会产生内存分配,并且分别对应了图中不同颜色的部分。而图的左边已经写出了产生内存分配的接口,右边则标明了释放内存的方法。 
  接下来详细看看每部分的含义。 
    

  • 黑色部分:www类本身占用内存,通过www接口加载AssetBundle才会有这部分内存,www对象保留了一份对WebStream数据(粉色部分)的引用。使用www =null 或者 www.dispose()释放。其中www.dispose()会立即释放,而www = null会等待垃圾回收。释放www后WebStream的引用计数会相应减一。

  • 橙色部分:官方称为WebStream数据,是数据真正的存储区域。当AssetBundle被加载进来后,这部分内存就被分配了。它包含3个内容:压缩后的AssetBundle本身、解压后的资源以及一个解压缓冲区(图绿)。无论www(黑色部分)还是后面会提到的AssetBundle对象(粉色部分),都只是有一个结构指向了WebStream数据,从而能对外部提供操作真正资源数据的方法。而当www对象和AssetBundle对象释放时,WebStream数据的引用计数也会相应减1。当WebStream数据引用计数为0时,系统会自动释放。但为了不频繁地开辟和销毁解压Buffer,其中绿色Decompression解压缓冲区Unity会至少保留一份。例如同时加载3个AssetBundle时,系统会生成3个Decompression Buffer,当解压完成后,系统会销毁两个。

  • 粉色部分:AssetBundle对象,引用了橙色WebStream数据部分,并提供了从WebStream数据中加载资源的接口。通过AssetBundle.Unload(bool unloadAllLoadedObjects)释放。如果调用AssetBundle.Unload(false),将释放AssetBundle对象本身,其对WebStream引用也将减少,从而可能引起WebStream释放,我们也就无法再通过接口或依赖关系从该AssetBundle加载资源。但已加载的资源还可以正常使用。如果调用的是AssetBundle.Unload(true),不仅会释放WebStream部分,所有被加载出来的资源将被释放。无论true或false,AssetBundle.Unload()都将销毁AssetBundle,销毁后调用该AssetBundle对象的任何方法都不会生效或产生报错,也就是说这个接口只能被调用一次,不能先调用unload(false)再调用unload(true)。

  • 红色部分:上图中红色部分不是非常清楚,我们换一张图: 
     
      这里紫色区域”AssetBundle文件内存镜像”就是上面的WebStream和www(黑橙)部分,虚线里的资源就是从AssetBundle加载出来的原始资源了,也属于WebStream范围,我们不再讨论。 
      而绿色实线内的资源(对应第一张图红色部分),也就是就是我们通过Instantiate()创建的GameObject所包含的资源。这些包含的资源又根据类型,与AssetBundle原始资源(WebStream资源部分)有不同的关系。有些如Texture、shader资源,我们通常只是使用,并不会对其做出改动,所以仅仅是引用关系;而每个GameObject都是特殊的,所以是完全复制了一份;至于Mesh和Material,则是引用+复制的关系。

建议

  • www加载完毕后调用www.dispose()或www = null。
  • 对于shader、通用图集等常驻且需要保留依赖关系的资源,在合适时机加载进来,不调用AssetBundle.Unload()接口。
  • 其他根据不同使用情况,选择AssetBundle.Unload()策略。 
    • 对于界面等存在明确生命周期,又可能动态加载的资源,在生命周期结束后调用AssetBundle.Unload(true),将全部资源一起释放。
    • 对于加载完后不再需要主动和被动依赖加载的资源,在加载完成后调用AssetBundel.Unload(false),立即释放掉AssetBundle资源,当资源使用完毕,代用Resources.UnloadAsset()或Resources.UnloadUnusedAssets()释放资源内存。
  • Resources.UnloadUnusedAssets()有较大消耗,尽量减少调用次数。
  • 不能调用AssetBundle.Unload(false)后再调用AssetBundle.Unload(true).
  • 不再使用的GameObject直接Destroy即可。

实测

  以上图片和解释来自于官方资料总结,但从加载接口可以看到是Unity5.3之前的版本,而在Unity5.3.4中是否真的如上面所言,我们仍需要“眼见为实”。毕竟没有亲测都是不可信的。 
  测试使用window平台,CPU i5-4750 3.2GHZ。从StreamingAsset下加载,测试时内存存在0.1M波动,但多次测试结果非常稳定。而加载耗时波动较大,不过不影响结论。 
  测试过程为:启动空的场景,先加载AssetBundle,再调用LoadAllAssets加载全部资源。在这个过程中,通过profile查看内存。 
  测试包含不压缩、LZMA压缩、LZ4压缩三种格式,压缩相关在Unity5-ABSystem(一):AssetBundle原理中有解释。 
  备注:unload(false)和unload(true)在一次测试中不能同时调用,所以其实是两次测试结果。由于表格显示关系,不要理解成unload(false)一次内存下降,unload(true)一次内存下降。例如下表第一行,调用前是203.3M,调用unload(true)直接降到37.4M,下降165.9M,但表格上标记(-82.1),仅仅是因为不想建立两个表格。

www加载实测

  使用www方式异步加载结果如下:

格式 ab
大小
启动
内存
www加载 LoadAllAssets dispose() unload(false) unload(true) 加载ab耗时 加载全部耗时

压缩
81.3M 37.4M 121.4M(+84) 203.3M(+81.9) 203.3M(+0) 119.5M(-83.8) 37.4M(-82.1) 99ms 66ms
LZMA 44.9M 37.4M 117.3M(+79.9) 199.2M(+81.9) 199.2M(+0) 1195M(-79.7) 37.4M(-82.1) 3377ms 119ms
LZ4 71.8M 37.4M 111.6(+74.2) 193.5M(+81.9) 193.5M(+0) 119.4M(-74.1) 37.4(-82) 99.7ms 176ms

  可以看到,使用www加载AssetBundle时,内存均有比未压缩之前ab包稍大的上涨,此时占用1倍AssetBundle大小内存。猜测这里www将整个AssetBundle原始文件均载入了内存,从加载耗时来看,如果是LZMA,还进行了3秒多的解压。这里不压缩居然内存涨幅最大,不知道www暗地里做了什么神奇的事情。 
  接着加载所有资源,非常整齐地都增长了81.9M。这部分就是我们希望使用的资源。但此时内存占用是我们资源的两倍左右。 
  测试中遇到比较奇怪的是,无论www.dispose或www = null是否调用,unload(false)均能释放WebStream数据内存,也就是上表中unload(false)减少部分。而调用www.dispose和www = null ,内存无明显变化。也就是并不需要释放www对WebStream的引用或www根本未引用WebStream。 
  与官方大佬描述不符,感到十分惶恐,贴出原书压压惊。 

  调用AssetBundle.Unload(true)之后,内存回到启动值。 
  最后,不压缩的AssetBundle加载资源最快,成绩为82M/66ms,LZMA即使已经被解压过了,加载时间仍然接近不压缩的两倍,而LZ4因为加载时解压,加载时间接近不压缩的三倍。

LoadFromFile加载实测

  使用LoadFromFile和LoadFromFileAsync接口加载,除了异步时间以外,其余差异不大,以LoadFromFile为例,测试结果如下。

格式 ab
大小
启动
内存
LoadFromFile LoadAllAssets unload(false) unload(true) 加载ab耗时 加载全部耗时

压缩
81.3M 38.5M 38.5M(+0) 120.5M(+82) 120.5M(+0) 38.5M(-82) 1.3ms 70.7ms
LZMA 44.9M 38.5M 118.5M(+80) 200.4M(+81.9) 120.5M(-79.9) 38.5M(-82) 3312ms 116ms
LZ4 71.8M 38.5M 38.5(+0) 120.5M(+82) 120.5M(+0) 38.5(-82) 1.2ms 179ms

  其中LZMA内存表现与www加载结果相近,最终仍然是AssetBundle本身+所有资源两倍内存,应该是它需要立即全部解压导致。而不压缩和LZ4格式,在加载AssetBundle时没有带来任何内存和时间上的额外开销,只有加载全部资源时,有资源内存的占用。同时对于这两种格式,调用unload(false)也没有任何效果,因为本身就从未分配过这部分内存。

建议

    • 无论使用何种方式压缩AssetBundle,均使用AssetBundle.LoadFromFile和AssetBundle.LoadFromFileAsync接口加载AssetBundle。避免使用www加载。
    • 避免使用LZMA压缩,有较长的解压时间和两倍实际资源的内存占用,尽量使用LZ4压缩(需要升级5.3或更高)。
    • 如果使用LZ4或不压缩包体过大,可以自己再对AssetBundle进行压缩,首次进入游戏时解压。

Unity5-ABSystem(五):AssetBundle内存的更多相关文章

  1. Unity5.X 新版AssetBundle打包控制

    一.什么是AssetBundle 估计很多人只知道Unity的模型之类的东西可以导出成一种叫做AssetBundle的文件,然后打包后可以在Unity程序运行的时候再加载出来用.那么AssetBund ...

  2. Unity5.x版本AssetBundle加载研究

    之前说了 “Unity5.x版本AssetBundle打包研究”,没看过的请先看一下:http://www.shihuanjue.com/?p=57 再来看本文,有一定的连接性. 先梳理一下思路: 要 ...

  3. 【STM32H7教程】第27章 STM32H7的TCM,SRAM等五块内存的动态内存分配实现

    完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第27章       STM32H7的TCM,SRAM等五块内 ...

  4. 【STM32H7教程】第26章 STM32H7的TCM,SRAM等五块内存的超方便使用方式

    完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980  第26章       STM32H7的TCM,SRAM等五块 ...

  5. 【STM32H7教程】第25章 STM32H7的TCM,SRAM等五块内存基础知识

    完整教程下载地址:http://www.armbbs.cn/forum.php?mod=viewthread&tid=86980 第25章       STM32H7的TCM,SRAM等五块内 ...

  6. Unity5.x版本AssetBundle打包研究

    Unity5的AssetBundle打包机制和以前版本不太一样.简单的说就是,只要给你要打包的资源设置一个AssetBundleName ,Unity自身会对这些设置了名字的资源进行打包,如果一个资源 ...

  7. Unity5版本的AssetBundle打包方案之打包Scene场景

    using UnityEngine; using System.Collections; using UnityEditor; /// <summary> /// 脚本位置:Editor文 ...

  8. Unity5系列资源管理AssetBundle——加载

    上次我们进行了AssetBundle打包,现在我们还把打包的资源加载到我们的游戏中.在加载之前,我们需要把打包好的Bundle包裹放到服务器上,如果没有,也可以使用XAMPP搭建本地服务器. 加载的A ...

  9. 【阅读笔记】《C程序员 从校园到职场》第五章 内存操作

    参考:   让你提前认识软件开发(8):memset()与memcpy()函数  https://blog.csdn.net/zhouzxi/article/details/22478081 让你提前 ...

随机推荐

  1. Linux下运行java项目

    最近初步接触了linux,感觉很有新鲜感.之前在windows下干过的事情也便想到在linux环境下实现一下.正好手头在编java,就想既然java可以在windows的DOS操作下运行,是不是也可以 ...

  2. $Java-json系列(二):用JSONObject解析和处理json数据

    本文中主要介绍JSONObject处理json数据时候的一些常用场景和方法. (一)jar包下载 所需jar包打包下载百度网盘地址:https://pan.baidu.com/s/1c27Uyre ( ...

  3. 40个你可能不知道的Python的特点和技巧

    1.拆箱 >>> a, b, c = 1, 2, 3 >>> a, b, c (1, 2, 3) >>> a, b, c = [1, 2, 3] ...

  4. nodejs文件追加内容

    const fs = require("fs"); // fs.appendFile 追加文件内容 // 1, 参数1:表示要向那个文件追加内容,只一个文件的路径 // 2, 参数 ...

  5. Logger日志级别说明及设置方法、说明

    日志记录器(Logger)是日志处理的核心组件.log4j具有5种正常级别(Level).日志记录器(Logger)的可用级别Level (不包括自定义级别 Level), 以下内容就是摘自log4j ...

  6. 谈谈 cci 与 i2c

    cci的名字叫, camera control interface, 他由两部分组成,一是i2c ,而另一个部分是 gpio.也就是说,cci 包含i2c.一般情况下,我们只是用到了i2c 部分,没有 ...

  7. Spark-运行时架构

    Spark运行时架构 在分布式环境下,Spark集群采用的时主/从结构.在一个Spark集群中,有一个节点负责中央协调,调度各个分布式工作节点.这个中央协调节点被称为驱动器(Driver),与之对应的 ...

  8. 轻松掌握XMLHttpRequest对象------【这是.net 版本】

    轻松掌握XMLHttpRequest对象 XmlHttp是什么? 最通用的定义为:XmlHttp是一套可以在Javascript.VbScript.Jscript等脚本语言中通过http协议传送或从接 ...

  9. 【转】php 操作数组(合并,拆分,追加,查找,删除等)

    1. 合并数组 array_merge()函数将数组合并到一起,返回一个联合的数组.所得到的数组以第一个输入数组参数开始,按后面数组参数出现的顺序依次迫加.其形式为: array array_merg ...

  10. DelphiXE

    1. http://blog.csdn.net/tp26021340/article/details/45953669 2. 3. 4. 5.