Q&A——内存管理(二)

内存管理

Q1:我们在Profiler中发现了一个AssetDatabase的内存占用,其存在于Assets中,单个占用内存非常大。 请问该占用是如何发生的呢?是否可以避免?
UWA Tech Doc

理论上开发者在真机运行项目时是不会看到此选项的,只有在 Editor 中运行时能看到。由于在 Editor 下运行游戏时,Profiler 中获取的内存是包含了 Editor 本身占用的内存的,因此并不准确,建议连接真机来进行内存的查看。

内存管理

Q2:同样的App,安装在不同的机型上,使用同样的自动化脚本进行自动化测试,App的内存消耗(ADB Dumpsys Meminfo)在不同的机型上结果迥异,有些机型只有200MB多的内存消耗,有些机型高达600MB。我们已经分析过OpenGL ES 2.0和OpenGL ES 3.0对ETC2贴图解压的影响。200MB左右的机型有些是ES2.0,600MB消耗的机型有些是ES3.0的,并且多次测试可重现。请问你们如何分析这个问题?是否有标准来确定游戏真正的内存消耗?

Android系统的内存消耗是根据芯片、OS的不同而不同的。ADB反馈出来的PSS其实仅是本机当前该App线程的内存消耗,该值与其他机器的信息其实是没有任何可比性的。

Unity的项目,其内存我们建议主要还是通过Profiler来看其内存消耗。因为它表示的是引擎开辟和分配的真实物理内存。


内存管理

Q3:首先用户或者产品只关心整体消耗,并不会以Profiler为准,内存不足的崩溃也不会以Profiler正常而消失。其次,我们降低Profiler统计的消耗后,OS的消耗也会大大降低,甚至下降的更多。我们据此判断:这两个数字不一样,但应该是有联系的。我们想知道联系是什么?

可以这样理解,Profiler 中的数字表示 Unity 向系统索要的内存值,而系统则会根据当前的内存情况、内存管理策略来进行分配,不同的硬件采取的策略是不同的(比如是否保留缓存,何时换页,内存回收的频率等等)。

PSS 就是获取当前的系统内存分配信息,这并不代表 app 的真实内存需求。
另外,还关乎 PSS 获取算法的具体实现,比如有些硬件不把显存计算在内,有些会计算等等。

关于PSS和Profiler之间的数值关系,我们认为确实是存在联系的,但该联系在不同的硬件上是不同的,且需要由硬件厂商来提供,这也是我们仅仅把 PSS 作为参考,把 Profiler 作为关注点的原因所在。

内存管理

Q4:Texture占用内存总是双倍,这个是我们自己的问题,还是Unity引擎的机制?

出现这种情况的原因有两种:一种是你在真机运行时开启了Read&Write。另一种可能是Unity的Bug,目前的Unity 5.2.3 release note如下 :
(735644) - OpenGL: Fixed texture memory usage reporting in profiler, was twice the actual size for most textures.
开发者需要关注下自己的开发版本,5.2.3以前类似情况的项目可以参考一下。

内存管理

Q5:在Unity的内存管理机制中, Reserved Total 和 Used Total之间的关系是怎样的?

Reserved Total 和 Used Total为Unity引擎在内存方面的总体分配量和总体使用量。 一般来说,引擎在分配内存时并不是向操作系统 “即拿即用”,而是首先获取一定量的连续内存,然后供自己内部使用,待空余内存不够时,引擎才会向系统再次申请一定量的连续内存进行使用。所以,从图表中可以看到,Reserved Total 的内存占用量略大于 Used Total, 且两者走势基本一致。

UWA Tech Doc

注意:对于绝大多数平台而言,Reseved Total内存 = Reserved Unity内存 + GFX内存 + FMOD内存 + Mono内存。(关于Unity的内存管理机制,请“阅读原文”跳转至“UWA文档”了解更多。同时,我们也会在以后的推送中开设内存专题,欢迎关注!)

内存管理

Q6:正常情况下游戏如果一直玩下去,Mono是不是会一直增加? 比如频繁打开一个界面,界面里有脚本会不断创建一些东西 ,那么Mono是否会不断增加?对性能上会不会造成影响呢?

A:Mono 确实是不会下降,但并不应该一直上升。
创建出来的东西,如果被引用在一个容器里,或者被某些脚本的变量引用,那么这部分堆内存就释放不掉;但如果没有被任何容器或者变量引用(比如,临时拼一个 String),那么这部分堆内存会在 GC 的时候释放(释放是指变为空闲的堆内存,堆内存的总量是不会下降的)。
对于后者,频繁地 new 对象虽然不会一直增加堆内存,但是会加速 GC 调用的频率,所以同样是需要尽量避免的。

内存管理

Q7:下图是UWA性能检测报告,图中标红的这两个函数常常导致很高的堆内存分配,那么我该如何检查或者避免呢?

UWA Tech Doc

Canvas.SendWillRenderCanvases为UGUI中非常重要的接口,经常会出现较高的性能开销。当Canvas中的UI元素出现了长、宽或Alpha变化时,UGUI会更新其所在Canvas中所有UI元素的Transform、状态等等。Canvas中UI Mesh顶点较多的话,则该项将会出现较高的CPU开销。

Loading.UpdatePreloading为Unity引擎的主要加载函数。场景中的资源加载(包括Texture、Mesh、Shader、AnimationClip等)和相关序列化操作均在其中体现。因此,如果该值开销较高,建议研发团队对资源进行进一步的优化和控制。


内存管理

Q8:如果脚本引用了GameObject,那转换场景的时候脚本和GameObject都没了,还会产生堆内存的吗?

如果脚本是MonoBehaviour,而且在切换场景后所挂的Game Object被释放了,那么这个脚本对象所引用的堆内存就会在GC的时候被释放。 但有一种例外,如果是通过Static变量引用的堆内存,那么依然是释放不掉的,除非手动解开引用,比如变量置Null,数组Clear等等。

内存管理

Q9:我们现在有一个场景,Draw Call和面数都在正常范围内,Camera的距离也和其他场景一样,但是VBO却非常高。下图是该场景的数据情况,该场景下有很多复用的模型,如果不勾选Static那VBO会下降,但是Draw Call会上升。那我能否通过合并模型的方式把VBO降下来呢?

UWA Tech Doc

当你勾选Static时,Unity 会将其进行 Static Batching,进而将生成一个较大的VBO来进行渲染,同时降低Draw Call。而如果不勾选时,则引擎无法对其进行Batch,所以VBO会降低,而Draw Call升高。

对此,我们的建议如下:
1、一般来讲,降低Draw Call的意义大于降低VBO;
2、在Draw Call较低的情况下,比如当前的50+,可以考虑适当增加一些Draw Call来降低VBO的占用,一方面可以降低带宽的压力,另一方面也可以适当降低一些内存。

内存管理

Q10:对于Handheld.PlayFullScreenMovie 这个Unity播放开场动画的API,会有内存问题吗?比如我的mp4动画有20MB,那么这个动画会撑高mono堆内存吗?

Android上PlayFullScreenMovie 的实现实际上是通过Android原生的接口直接播放的,播放过程中Unity也是停止更新的,因此这部分的内存理论上并不会记录在 Unity 中,同样也不影响mono。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值