Unity内存管理:AB的内存处理

2021/11/5 7:10:15

本文主要是介绍Unity内存管理:AB的内存处理,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

一、经典的对称造型,用多少释放多少。

这是各阶段的内存和其他数据变化

说明:

  1. 初始状态
  2. AssetBundle.Load文件后:内存多了文件镜像,Memory+4.4MB,Total Object和Assets+1【AssetBundle也是object】
  3. 载入Texture后: Memory+4MB,因为多了Texture Asset占用的内存,Total Objects和Assets+1
  4. 载入Prefab后:内存无明显变化,因为最占内存的Texture已经加载,Materials+1是因为多了Prefab的材质,Total Objects和Assets+6,因为 Perfab 包含很多 Components
  5. 实例化Prefab以后:显存(Texture Memory+4MBGameObject、Total Objects in Scene++,都是因为实例化了一个可视的对象
  6. 下面是开始对称的操作:销毁
  7. 销毁实例后:Texture Memory-4MB,GameObject、Total Objects in Scene--
  8. 卸载AssetBundle文件后:AssetBundle文件镜像占用的内存被释放Memory-4.3MB(??泄露了?,相应的Assets和Total Objects Count-1
  9. 直接Resources.UnloadUnusedAssets:没有任何变化,因为所有Assets引用并没有清空
  10. 把Prefab引用变量设为null以后,再Resources.UnloadUnusedAssets:整个Prefab除了Texture外都没有任何引用了,所以被UnloadUnusedAssets销毁,Assets和Total Objects Count -6Materials-1
  11. 再把Texture的引用变量设为null,再Resources.UnloadUnusedAssets :之后也被UnloadUnusedAssets销毁,内存被释放Texture Memory-4MBassets和Total Objects Count -1,基本还原到初始状态

总结:

ab加载完(AssetBundle.Load),内存++【多了文件镜像】
实例化预制(Instance),显存++【多了可视对象】
Texture加载以后是到内存,显示的时候才进入显存的Texture Memory。
所有的东西基础都是Object
Load的是Asset,Instantiate的是GameObject和Object in Scene
Load的Asset要Unload才能卸载干净,new的或者Instantiate的object可以直接Destroy

一、ab加载与释放

  1. AssetBundle.CreateFromFile运行时加载:只是个AssetBundle内存镜像数据块
  2. AssetBundle.Load(同Resources.Load) 这才会从AssetBundle的内存镜像里读取并创建一个Asset对象,创建Asset对象同时也会分配相应内存用于存放(反序列化)
  3. AssetBundle的释放:
AssetBundle.Unload( flase)是释放AssetBundle文件的内存镜像, 不包含Load创建的Asset内存对象。
AssetBundle.Unload(true)是释放那个AssetBundle文件内存镜像和并销毁所有用Load创建的Asset内存对象。这个蛮重要的,不注意的话会造成材质引用丢失。

二、预制实例化

一个Prefab从assetBundle里Load出来,里面可能包括:Gameobject、transform、mesh、texture、material、shader、script和各种其他Assets

你实例化一个Prefab,其实是一个对Assets进行Clone(复制)+引用结合的过程

  • 其他mesh / texture / material / shader 等,这其中些是纯引用的关系的,包括:Texture和TerrainData
  • 还有引用和复制同时存在的,包括:Mesh/material /PhysicMaterial。
  • 引用的Asset对象不会被复制,只是一个简单的指针指向已经Load的Asset对象。这种含糊的引用加克隆的混合, 大概是搞糊涂大多数人的主要原因。

总结一下各种释放

  • Destroy: 主要用于销毁克隆对象,也可以用于场景内的静态物体,不会自动释放该对象的所有引用。虽然也可以用于Asset,但是概念不一样要小心,如果用于销毁从文 件加载的Asset对象会销毁相应的资源文件!但是如果销毁的Asset是Copy的或者用脚本动态生成的,只会销毁内存对象。
  • AssetBundle.Unload(false):释放AssetBundle文件内存镜像
  • AssetBundle.Unload(true):释放AssetBundle文件内存镜像同时销毁所有已经Load的Assets内存对象
  • Reources.UnloadAsset(Object):显式的释放已加载的Asset对象,只能卸载磁盘文件加载的Asset对象
  • Resources.UnloadUnusedAssets():用于释放所有没有引用的Asset对象
  • GC.Collect():强制垃圾收集器立即释放内存 Unity的GC功能不算好,没把握的时候就强制调用一下

举两个例子帮助理解

例子1:

一个常见的错误:

你从某个AssetBundle里Load了一个prefab并克隆:

obj = Instaniate(AssetBundle1.Load('MyPrefab”);

这个prefab比如是个npc。然后你不需要他的时候你用了:Destroy(obj);你以为就释放干净了。其实这时候只是释放了Clone对象,通过Load加载的所有引用、非引用Assets对象全都静静静的躺在内存里。

这种情况应该在Destroy以后用:AssetBundle1.Unload(true),彻底释放干净。

  1. 如果这个AssetBundle1是要反复读取的。不方便Unload,那可以在Destroy以后用:Resources.UnloadUnusedAssets(); 把所有和这个npc有关的Asset都销毁。
  2. 当然如果这个NPC也是要频繁创建销毁的,那就应该让那些Assets呆在内存里以加速游戏体验。

由此可以解释另一个之前有人提过的话题:为什么第一次Instaniate一个Prefab的时候都会卡一下?

因为在你第一次Instaniate之前,相应的Asset对象还没有被创建,要加载系统内置的 AssetBundle并创建Assets。第一次以后你虽然Destroy了,但Prefab的Assets对象都还在内存里,所以就很快了。

例子2:

从磁盘读取一个1.unity3d文件到内存并建立一个AssetBundle1对象

AssetBundle AssetBundle1 = AssetBundle.CreateFromFile("1.unity3d");

从AssetBundle1里读取并创建一个Texture Asset,把obj1的主贴图指向它

obj1.renderer.material.mainTexture = AssetBundle1.Load("wall") as Texture;

把obj2的主贴图也指向同一个Texture Asset

obj2.renderer.material.mainTexture =obj1.renderer.material.mainTexture;

Texture是引用对象,永远不会有自动复制的情况出现(除非你真需要,用代码自己实现copy),只会是创建和添加引用

  1. 如果继续:AssetBundle1.Unload(true) 那obj1和obj2都变成黑的了,因为指向的Texture Asset没了
  2. 如果:AssetBundle1.Unload(false) 那obj1和obj2不变,只是AssetBundle1的内存镜像释放了
  3. 继续:Destroy(obj1); //obj1被释放,但并不会释放刚才Load的Texture
  4. 如果这时候:Resources.UnloadUnusedAssets();//不会有任何内存释放 因为Texture asset还被obj2引用着
  5. 如果Destroy(obj2);//obj2被释放,但也不会释放刚才Load的Texture
  6. 继续Resources.UnloadUnusedAssets();//这时候刚才load的Texture Asset释放了,因为没有任何引用了
  7. 最后CG.Collect();强制立即释放内存

由此可以引申出论坛里另一个被提了几次的问题:

如何加载一堆大图片轮流显示又不爆掉?

不考虑AssetBundle,直接用www读图片文件的话等于是直接创建了一个Texture Asset 假设文件保存在一个List里

TLlist<string> fileList;
int n=0;
IEnumerator OnClick()
{
    WWW image = new www(fileList[n++]);
    yield return image;
    Texture tex = obj.mainTexture;
    obj.mainTexture = image.texture;
    n = (n>=fileList.Length-1)?0:n;
    Resources.UnloadAsset(tex);
}

这样卸载比较快

问题1:先说一个遇到的坑,当大量(几百个)AssetBundle加载的时候(可能是WWW加载的时候,也可能是AssetBundle.LoadAsset的时候),Android手机上会闪退。

看崩溃log是多线程文件访问的时候崩溃了。
解决方法是减少同时加载的AB数量(这个是纯逻辑控制),使用的是AssetBundle.LoadFromFile接口。

 【Unity游戏开发】AB学习(三)加载和实例化的内存变化 - 知乎



这篇关于Unity内存管理:AB的内存处理的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程