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

【Unity游戏开发】AB学习(三)加载和实例化的内存变化前言了解一下加载AB和实例化操作对应的内存变化,有助于我们更好的理解内存管理这篇文章会详尽地举例说明,帮助理解加载一个AB后,内存对应的变化情况一、引入:经典的对称造型,用多少释放多少

前言

了解一下
加载AB和实例化操作对应的内存变化,有助于我们更好的理解内存管理

这篇文章会详尽地举例说明,帮助理解加载一个AB后,内存对应的变化情况

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

以下是加载AB和实例化各阶段的内存和其他数据变化

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

详细说明:

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

二、Load 和 Instantiate

2.1 AB加载与释放

  • AssetBundle.Load(同Resources.Load): 从AssetBundle的内存镜像里读取并创建一个Asset对象,分配相应内存用于存放

2.2 预制实例化 Instantiate

一个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对象。这种含糊的引用加克隆的混合, 大概是搞糊涂大多数人的主要原因。

三、总结一下各种释放

3.0 概念

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

AssetBundle 加载 Asset 并 实例化 的流程

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

注意2个概念

AssetBundle.Unload

Unloads assets in the bundle.


When unloadAllLoadedObjects is false, compressed file data for assets inside the bundle will be unloaded, but any actual objects already loaded from this bundle will be kept intact. Of course you won’t be able to load any more objects from this bundle.


When unloadAllLoadedObjects is true, all objects that were loaded from this bundle will be destroyed as well. If there are GameObjects in your Scene referencing those assets, the references to them will become missing.

AssetBundle.Unload(
flase)是释放AssetBundle文件的内存镜像,
不包含Load创建的Asset内存对象。

AssetBundle.Unload(true)是释放那个AssetBundle文件内存镜像和并销毁所有用Load创建的Asset内存对象。这个蛮重要的,不注意的话会造成材质引用丢失。

Resources.UnloadAsset

This function can only be called on Assets that are stored on disk.


The referenced asset (assetToUnload) will be unloaded from memory. The object will become invalid and can’t be loaded back from disk. Any subsequently loaded Scenes or assets that reference the asset on disk will cause a new instance of the object to be loaded from disk. This new instance will not be connected to the previously unloaded object.


Resources.UnloadAsset仅能释放非GameObject和Component的资源,比如Texture、Mesh等真正的资源。对于由Prefab加载出来的Object或Component,则不能通过该函数来进行释放。

对于用户来说,如果选择 AssetBundle.Unload(true),用户必须确保 Bundle 中已经加载的 资源 是没有被引用的,否则就会发生 资源丢失

如果选择 AssetBundle.Unload(false),用户就要承担起卸载 已加载资源 的责任,如果处理不当,就可能造成 资源重复,如下图:

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

最后,Unity提供了一个 Resources.UnloadUnusedAssets 接口帮助我们销毁没有任何引用的 野资源,不过这个函数会扫描全部对象,开销较大,一般只在 切场景 时调用。

3.1 举两个例子帮助理解

3.1 例子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呆在内存里以加速游戏体验。

由此可以解释另一个之前有人提过的话题:

3.1 问题1:为什么第一次Instaniate一个Prefab的时候都会卡一下?

因为在你第一次Instaniate之前,相应的Asset对象还没有被创建,要加载系统内置的 AssetBundle并创建Assets。

第一次以后你虽然Destroy了,但Prefab的Assets对象都还在内存里,所以就很快了。

3.2 例子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();强制立即释放内存

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

3.2 问题2:如何加载一堆大图片轮流显示又不爆掉?

不考虑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);
}

这样卸载比较快

四、踩坑归纳

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

看崩溃log是多线程文件访问的时候崩溃了。

解决方法是减少同时加载的AB数量(这个是纯逻辑控制),使用的是AssetBundle.LoadFromFile接口。

AB卸载策略

  • 界面加载AB后,实例化,然后直接卸载界面AB
  • 界面关闭卸载界面AB
  • Image在OnDestoy的时候卸载图片AB

参考:

Unity3D内存释放 (很详细的)

【Unity游戏开发】AssetBundle杂记–AssetBundle的二三事 – 云+社区 – 腾讯云

恶毒的狗 | Bad Fat Dog

关于AssetBundle的卸载

今天的文章【Unity游戏开发】AB学习(三)加载和实例化的内存变化分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/83972.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注