unity 查看内存占用游戏后台后内存会不会释放

1.资源类型
GameObject, Transform, Mesh, Texture, Material, Shader, Script和各种其他Assets。
2.资源创建方式
静态引用,在脚本中加一个public GameObject变量,在Inspector面板中拖一个prefab到该变量上,然后在需要引用的地方Instantiate;Resource.Load,资源需要放在Assets/Resources目录下;AssetBundle.Load, Load之后Instantiate。
3. 资源销毁方式
GameObject.Destroy(gameObject),销毁该物体;AssetBundle.Unload(false),释放AssetBundle文件内存镜像,不销毁Load创建的Assets对象;AssetBundle.Unload(true),释放AssetBundle文件内存镜像同时销毁所有已经Load的Assets内存镜像;Resources.UnloadAsset(Object),释放已加载的Asset对象;Resources.UnloadUnusedAssets,释放所有没有引用的Asset对象。
4. 生命周期
实验中创建了一个简单场景,场景中创建了一个Empty GameObject,上面挂了一个脚本,在Awake函数中通过协程函数来创建资源,具体的Coroutine函数下面都有。
实验中创建的Prefab是一个坦克车,加入场景中场景内存增加3M左右,同时创建了一个AssetBundle资源供AssetBundle使用。
1. Resources.Load方式加载一个Prefab, 然后Instantiate GameObject
代码如下:
IEnumerator LoadResources()
Resources.UnloadUnusedAssets();
yield return new WaitForSeconds(5.0f);
GameObject tank = Resources.Load(&Role/Tank&) as GameO
yield return new WaitForSeconds(0.5f);
GameObject tankInst = GameObject.Instantiate(tank, Vector3.zero, Quaternion.identity) as GameO
yield return new WaitForSeconds(0.5f);
GameObject.Destroy(tankInst);
yield return new WaitForSeconds(0.5f);
tank = null;
Resources.UnloadUnusedAssets();
yield return new WaitForSeconds(0.5f);
执行结果如下:
下面是统计结果:
GameObjects
Objects in Scene
Total Objects
Resources.Load
Instantiate
Resources.UnloadUnusedAssets
从这里我们得出如下结论:
Resouces.Load一个Prefab相对于Instantiate一个资源来说是相对轻量的一个操作,上述过程中,Resources.Load加载一个Prefab几乎没有消耗内存,而Instantiate消耗了2.5M的资源空间。Resources.Load增加了Mesh和Total Object的数量,而Instantiate增加了GameObjects,Objects In Scene和Total Objects的数量;Destroy一个GameObject之后,内存有所减少,但是比较少,本例中减少了0.6M;Instantiate和Destroy前后Material和Texture没有还原,用以后面继续进行Instantiate之用。
若没有调用Resources.UnloadUnusedAssets,则结果如下:
统计结果如下:
GameObjects
Objects in Scene
Total Objects
Resources.Load
Instantiate
得出如下结论:
如果不手动执行Resources.UnloadUnusedAssets,则多余的Mesh,Material和Object不会主动释放。
2. 以AssetBundle.Load的方式加载一个Prefab,然后Instantiate一个GameObject
代码如下:
IEnumerator LoadAssets(string path)
Resources.UnloadUnusedAssets();
yield return new WaitForSeconds(5.0f);
WWW bundle = new WWW(path);
yield return
yield return new WaitForSeconds(0.5f);
bundle.assetBundle.Load(&tank&);
yield return new WaitForSeconds(0.5f);
GameObject tankInst = Instantiate(obj) as GameO
yield return new WaitForSeconds(0.5f);
GameObject.Destroy(tankInst);
yield return new WaitForSeconds(0.5f);
bundle.assetBundle.Unload(false);
yield return new WaitForSeconds(0.5f);
yield return new WaitForSeconds(0.5f);
执行结果如下:
统计结果如下:
GameObjects
Objects in Scene
Total Objects
AssetBundle.Load
Instantiate
AssetBundle.Unload
Resources.UnloadUnusedAssets
得出如下结论:
通过WWW Load AssetBundle的方式加载一个资源时会自动加载相应的Mesh,Texture和Material,而通过Resouces.Load方式进行加载只会加载Mesh信息。因此通过AssetBundle方式加载后Instantiate一个资源的内存消耗较小,本例中AssetBundle.Load增加了2.5M的内存,而Instantiate增加了1.1M的内存。相比较Resources.Load后Instantiate的内存增量要小很多。
3. 通过静态绑定的方法来Instantiate一个资源
代码如下:
IEnumerator InstResources()
Resources.UnloadUnusedAssets();
yield return new WaitForSeconds(5.0f);
GameObject inst = GameObject.Instantiate(tank, Vector3.zero, Quaternion.identity) as GameO
yield return new WaitForSeconds(1f);
GameObject.Destroy(inst);
yield return new WaitForSeconds(1f);
tank = null;
Resources.UnloadUnusedAssets();
yield return new WaitForSeconds(1f);
执行结果如下:
统计结果如下:
GameObjects
Objects in Scene
Total Objects
Instantiate
UnloadUnused Resources
得出结论如下:
通过静态绑定的方式各种资源的加载顺序和Resources.Load的方式是一样的,一个GameObject创建时,其Component中静态绑定的GameObject只会加载Mesh信息,只有当该GameObject Instantiate出来之后才会加载Texture和Material信息。
加载资源的过程可以分为两个阶段,第一阶段是使用Resources.Load或者AssetBundle.Load加载各种资源,第二阶段是使用GameObject.Instantiate克隆出一个新的GameObject。
Load的资源类型包括GameObject, Transform, Mesh, Texture, Material, Shader和Script等各种资源,但是Resources.Load和AssetBundle.Load是有区别的。
使用Resources.Load的时候在第一次Instantiate之前,相应的Asset对象还没有被创建,直到第一次Instantiate时才会真正去读取文件创建这些Assets。它的目的是实现一种OnDemand的使用方式,到该资源真正使用时才会去创建这些资源。
而使用AssetBundle.Load方法时,会直接将资源文件读取出来创建这些Assets,因此第一次Instantiate的代价会相对较小。
上述区别可以帮助我们解释为什么发射第一发子弹时有明显的卡顿现象的出现。
然后我们再来了解一下Instantiate的过程。Instantiate的过程是一个对Assets进行Clone(复制)和引用相结合的过程,Clone的过程需要申请内存存放自己的数据,而引用的过程只需要直接一个简单的指针指向一个已经Load的资源即可。例如Transform是通过Clone出来的,Texture和TerrainData是通过引用复制的,而Mesh,Material,PhysicalMaterial和Script是Clone和引用同时存在的。以Script为例,Script分为代码段和数据段,所有需要使用该Script的GameObject使用的代码是一样的,而大家的数据有所区别,因此对数据段需要使用Clone的方式,而对代码段需要使用引用的方式来复制。
因此Load操作其实Load一些数据源出来,用于创建新对象时被Clone或者被引用。
然后是销毁资源的过程。当Destory一个GameObject或者其他实例时,只是释放实例中那些Clone出来的Assets,而并不会释放那些引用的Assets,因为Destroy不知道是否有其他人在引用这些Assets。等到场景中没有任何物体引用到这些Assets之后,它们就会成为UnusedAssets,此时可以通过Resources.UnloadUnusedAssets来进行释放。AssetBundle.Unload(false)不行,因为它只会释放文件的内存镜像,不会释放资源;AssetBunde.Unload(true)也不行,因为它是暴力的释放,可能有其他对象在引用其中的Assets,暴力释放可能导致程序错误。
另外需要注意,系统在加载新场景时,所有的内存对象都会被自动销毁,这包括了Resources.Load加载的Assets, 静态绑定的Assets,AssetBundle.Load加载的资源和Instantiate实例化的对象。但是AssetBundle.Load本身的文件内存镜像(用于创建各种Asset)不会被自动销毁,这个必须使用AssetBundle.Unload(false)来进行主动销毁。推荐的做法是在加载完资源后立马调用AssetBunble.Unload(false)销毁文件内存镜像。
下图可以帮助理解内存中的Asset和GameObject的关系。
为了不出现首次Instantiate时卡顿的现象,推荐使用AssetBundle.Load的方式代替Resources.Load的方式来加载资源;加载完资源后立马调用AssetBunble.Unload(false)释放文件内存镜像;Unity自身没有提供良好的内存申请和释放管理机制,Destroy一个GameObject会马上释放内存而不是进行内部的缓存,因此应用程序对频繁不用的对象如NPC,FX等进行对象池管理是必要的,减少内存申请次数;何时进行Resources.UnloadUnusedAssets是需要讨论的一个问题。
本文已收录于以下专栏:
相关文章推荐
Unity手游的性能优化过程更像是一门时空转换的艺术, 持续在CPU和内存之间取得一个平衡。空间不足时则需要释放一些无用数据,以获得更优的空间使用率;时间太长时就需要降低不必要的函数开销。以下是腾讯游...
Resources.Load:使用这种方式加载资源,首先需要下Asset目录下创建一个名为Resources的文件夹,这个命名是U3D规定的方式,然后把资源文件放进去,当然也可以在Resources中...
1.C#的文档注释
图形建模, 矢量图编辑, 科学分析VC++源代码, 数据采集与显示组件库, 软件, VC++源代码
-- 100%源码全开放企业级制图\打印\流程...
推荐地址:
/U-tansuo/archive//unity3d_memory_optimization.html
首先:Un...
最近网友通过网站搜索Unity3D在手机及其他平台下占用内存太大. 这里写下关于Unity3D对于内存的管理与优化.
Unity3D 里有两种动态加载机制:一个是Resources.Load,另...
Texture图片空间和内存占用分析。由于U3D并没有很好的诠释对于图片的处理方式,所以很多人一直对于图集的大小和内存的占用情况都不了解。在此对于U3D的图片问题做一个实际数据的分析。此前的项目都会存...
内存是Unity手游的硬伤,如果没有做好内存的相关管理和详细的测试,游戏极有可能出现卡顿、闪退等影响用户体验的现象。在此,笔者为我们介绍了一些Unity手游内存分析和测试过程中比较实用的测试场景案例、...
Texture图片空间和内存占用分析。由于U3D并没有很好的诠释对于图片的处理方式,所以很多人一直对于图集的大小和内存的占用情况都不了解。在此对于U3D的图片问题做一个实际数据的分析。此前的项目都会存...
以图行可视化方式直观显示资源所占内存百分比[包括模型、贴图、动画序列、字体等]
以索引列表说明当前游戏对象之间的引用关系,[包括类名、ID、销毁状态等]
方便非专业技术人员比如TA、美术设计师查看当前...
他的最新文章
讲师:汪剑
讲师:刘道宽
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)Unity内存申请和释放 - 简书
Unity内存申请和释放
1.资源类型
GameObject, Transform, Mesh, Texture, Material, Shader, Script和各种其他Assets。
2.资源创建方式
静态引用,在脚本中加一个public GameObject变量,在Inspector面板中拖一个prefab到该变量上,然后在需要引用的地方Instantiate;
Resource.Load,资源需要放在Assets/Resources目录下;
AssetBundle.Load, Load之后Instantiate。3. 资源销毁方式
GameObject.Destroy(gameObject),销毁该物体;
AssetBundle.Unload(false),释放AssetBundle文件内存镜像,不销毁Load创建的Assets对象;
AssetBundle.Unload(true),释放AssetBundle文件内存镜像同时销毁所有已经Load的Assets内存镜像;
Resources.UnloadAsset(Object),释放已加载的Asset对象;
Resources.UnloadUnusedAssets,释放所有没有引用的Asset对象。4. 生命周期
实验中创建了一个简单场景,场景中创建了一个Empty GameObject,上面挂了一个脚本,在Awake函数中通过协程函数来创建资源,具体的Coroutine函数下面都有。实验中创建的Prefab是一个坦克车,加入场景中场景内存增加3M左右,同时创建了一个AssetBundle资源供AssetBundle使用。1. Resources.Load方式加载一个Prefab, 然后Instantiate GameObject
代码如下:
IEnumerator LoadResources()
// 清除干净以免影响测试结果
Resources.UnloadUnusedAssets();
// 等待5秒以看到效果
yield return new WaitForSeconds(5.0f);
// 通过Resources.Load加载一个资源
GameObject tank = Resources.Load("Role/Tank") as GameO
yield return new WaitForSeconds(0.5f);
// Instantiate一个资源出来
GameObject tankInst = GameObject.Instantiate(tank, Vector3.zero, Quaternion.identity) as GameO
yield return new WaitForSeconds(0.5f);
// Destroy一个资源
GameObject.Destroy(tankInst);
yield return new WaitForSeconds(0.5f);
//释放无用资源
Resources.UnloadUnusedAssets();
yield return new WaitForSeconds(0.5f);
执行结果如下:
下面是统计结果:
GameObjects
Objects in Scene
Total Objects
Resources.Load
Instantiate
Resources.UnloadUnusedAssets
从这里我们得出如下结论:
Resouces.Load一个Prefab相对于Instantiate一个资源来说是相对轻量的一个操作,上述过程中,Resources.Load加载一个Prefab几乎没有消耗内存,而Instantiate消耗了2.5M的资源空间。Resources.Load增加了Mesh和Total Object的数量,而Instantiate增加了GameObjects,Objects In Scene和Total Objects的数量;
Destroy一个GameObject之后,内存有所减少,但是比较少,本例中减少了0.6M;Instantiate和Destroy前后Material和Texture没有还原,用以后面继续进行Instantiate之用。
若没有调用Resources.UnloadUnusedAssets,则结果如下:
统计结果如下:
GameObjects
Objects in Scene
Total Objects
Resources.Load
Instantiate
得出如下结论:如果不手动执行Resources.UnloadUnusedAssets,则多余的Mesh,Material和Object不会主动释放。
2. 以AssetBundle.Load的方式加载一个Prefab,然后Instantiate一个GameObject
代码如下:
IEnumerator LoadAssets(string path)
// 清除干净以免影响测试结果
Resources.UnloadUnusedAssets();
// 等待5秒以看到效果
yield return new WaitForSeconds(5.0f);
// 创建一个WWW类
WWW bundle = new WWW(path);
yield return new WaitForSeconds(0.5f);
// AssetBundle.Load一个资源
bundle.assetBundle.Load("tank");
yield return new WaitForSeconds(0.5f);
// Instantiate一个资源出来
GameObject tankInst = Instantiate(obj) as GameO
yield return new WaitForSeconds(0.5f);
// Destroy一个资源
GameObject.Destroy(tankInst);
yield return new WaitForSeconds(0.5f);
// Unload Resources
bundle.assetBundle.Unload(false);
yield return new WaitForSeconds(0.5f);
// 释放无用资源
//Resources.UnloadUnusedAssets();
yield return new WaitForSeconds(0.5f);
执行结果如下:
统计结果如下:
GameObjects
Objects in Scene
Total Objects
AssetBundle.Load
Instantiate
AssetBundle.Unload
Resources.UnloadUnusedAssets
得出如下结论:通过WWW Load AssetBundle的方式加载一个资源时会自动加载相应的Mesh,Texture和Material,而通过Resouces.Load方式进行加载只会加载Mesh信息。因此通过AssetBundle方式加载后Instantiate一个资源的内存消耗较小,本例中AssetBundle.Load增加了2.5M的内存,而Instantiate增加了1.1M的内存。相比较Resources.Load后Instantiate的内存增量要小很多。
3. 通过静态绑定的方法来Instantiate一个资源
代码如下:
IEnumerator InstResources()
Resources.UnloadUnusedAssets();
yield return new WaitForSeconds(5.0f);
GameObject inst = GameObject.Instantiate(tank, Vector3.zero, Quaternion.identity) as GameO
yield return new WaitForSeconds(1f);
GameObject.Destroy(inst);
yield return new WaitForSeconds(1f);
//释放无用资源
Resources.UnloadUnusedAssets();
yield return new WaitForSeconds(1f);
执行结果如下:
统计结果如下:
GameObjects
Objects in Scene
Total Objects
Instantiate
UnloadUnused Resources
得出结论如下:通过静态绑定的方式各种资源的加载顺序和Resources.Load的方式是一样的,一个GameObject创建时,其Component中静态绑定的GameObject只会加载Mesh信息,只有当该GameObject Instantiate出来之后才会加载Texture和Material信息。
加载资源的过程可以分为两个阶段,第一阶段是使用Resources.Load或者AssetBundle.Load加载各种资源,第二阶段是使用GameObject.Instantiate克隆出一个新的GameObject。Load的资源类型包括GameObject, Transform, Mesh, Texture, Material, Shader和Script等各种资源,但是Resources.Load和AssetBundle.Load是有区别的。使用Resources.Load的时候在第一次Instantiate之前,相应的Asset对象还没有被创建,直到第一次Instantiate时才会真正去读取文件创建这些Assets。它的目的是实现一种OnDemand的使用方式,到该资源真正使用时才会去创建这些资源。而使用AssetBundle.Load方法时,会直接将资源文件读取出来创建这些Assets,因此第一次Instantiate的代价会相对较小。上述区别可以帮助我们解释为什么发射第一发子弹时有明显的卡顿现象的出现。
然后我们再来了解一下Instantiate的过程。Instantiate的过程是一个对Assets进行Clone(复制)和引用相结合的过程,Clone的过程需要申请内存存放自己的数据,而引用的过程只需要直接一个简单的指针指向一个已经Load的资源即可。例如Transform是通过Clone出来的,Texture和TerrainData是通过引用复制的,而Mesh,Material,PhysicalMaterial和Script是Clone和引用同时存在的。以Script为例,Script分为代码段和数据段,所有需要使用该Script的GameObject使用的代码是一样的,而大家的数据有所区别,因此对数据段需要使用Clone的方式,而对代码段需要使用引用的方式来复制。因此Load操作其实Load一些数据源出来,用于创建新对象时被Clone或者被引用。
然后是销毁资源的过程。当Destory一个GameObject或者其他实例时,只是释放实例中那些Clone出来的Assets,而并不会释放那些引用的Assets,因为Destroy不知道是否有其他人在引用这些Assets。等到场景中没有任何物体引用到这些Assets之后,它们就会成为UnusedAssets,此时可以通过Resources.UnloadUnusedAssets来进行释放。AssetBundle.Unload(false)不行,因为它只会释放文件的内存镜像,不会释放资源;AssetBunde.Unload(true)也不行,因为它是暴力的释放,可能有其他对象在引用其中的Assets,暴力释放可能导致程序错误。另外需要注意,系统在加载新场景时,所有的内存对象都会被自动销毁,这包括了Resources.Load加载的Assets, 静态绑定的Assets,AssetBundle.Load加载的资源和Instantiate实例化的对象。但是AssetBundle.Load本身的文件内存镜像(用于创建各种Asset)不会被自动销毁,这个必须使用AssetBundle.Unload(false)来进行主动销毁。推荐的做法是在加载完资源后立马调用AssetBunble.Unload(false)销毁文件内存镜像。下图可以帮助理解内存中的Asset和GameObject的关系。
为了不出现首次Instantiate时卡顿的现象,推荐使用AssetBundle.Load的方式代替Resources.Load的方式来加载资源;
加载完资源后立马调用AssetBunble.Unload(false)释放文件内存镜像;
Unity自身没有提供良好的内存申请和释放管理机制,Destroy一个GameObject会马上释放内存而不是进行内部的缓存,因此应用程序对频繁不用的对象如NPC,FX等进行对象池管理是必要的,减少内存申请次数;
何时进行Resources.UnloadUnusedAssets是需要讨论的一个问题。Unity优化相关总结(内存) - SHY不小心 - CSDN博客
Unity优化相关总结(内存)
内存是游戏中比较重要的一项,如果没有做好内存的管理,游戏可能会出现卡顿甚至闪退的现象。
Unity中的内存开销大致可以分为三类:1.资源内存占用 &2.引擎自身内存占用 &3.托管堆内存占用
其中资源内存占用可参考上篇Unity优化相关总结(资源),引擎自身内存占用了解不是很深,还需要进一步学习,
今天主要来说一下托管堆内存。
一.托管堆内存(Mono内存)
基于Unity开发而言,托管堆内存也可以认为是Mono内存,托管堆内存是由Mono分配和管理的。
托管的本意是Mono可以自动地改变堆的大小来适应所需要的内存。
但是这里有一点需要注意:虽然Mono会自动的改变堆内存大小,但是在代码中也不可以随意的开辟托管堆内存。
因为在Unity中,Mono的堆内存一旦被分配,就不会返还给系统,也就是说,Mono的堆内存会只升不降。
二.Mono内存管理策略
Mono的内存分为两个部分,已用内存+保留内存
Mono通过垃圾回收机制(GC)对内存进行管理
Mono中的GC主要有以下几个步骤
1.停止所有需要Mono内存分配的线程
2.遍历所有已用内存,找到那些不需要使用的内存(通过引用方式进行判断),并进行标记
3.释放被标记的内存
4.重新开始被停止的线程
三.使用Mono内存中存在的问题
1.Mono内存分配过大
在程序运行过程中,我们应该避免分配不必要的Mono堆内存
1.避免在Update、FixedUpdate或者高频率使用的函数中开辟堆内存(如 new Class /Array等)
2.避免在Update、FixedUpdate或者高频率使用的函数中使用GetComponent&&函数(单次使用不会有太多GC产生,但是数量级上来后会产生很多GC Alloc)
3.避免在Update、FixedUpdate或者高频率使用的函数中使用obj.name的操作(原因同上)
4.避免在Update、FixedUpdate或者高频率使用的函数中进行字符串操作,使用stringBuilder代替String
5.避免在Update、FixedUpdate或者高频率使用的函数中使用foreach函数
6.尽量不使用OnGUI函数,GUI.repaint也会产生一定数量的GC Alloc
7.Log输出需要进行自己的封装管理,打包的时候关闭Log输出
8.配置表的管理,有效的管理项目的配置表,可以节约可观的堆内存
2.Mono内存泄漏
Mono内存泄漏就是已经不被需要的对象没有被GC回收。
Mono内存泄漏会使空闲内存减少,GC频繁,Mono堆内存不断的扩充,最终导致游戏内存占用升高。
针对于Mono内存泄漏,就需要好好的对代码进行排查了。
我的热门文章}

我要回帖

更多关于 unity 内存管理 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信