PSS内存优化方法

PSS内存优化方法

这是第170篇UWA技术知识分享的推送。今天我们继续为大家精选了若干和开发、优化相关的问题,建议阅读时间20分钟,认真读完必有收获。

UWA 问答社区:answer.uwa4d.com
UWA QQ群2:793972859(原群已满员)

本期目录:

  • PSS内存优化方法
  • UWA GOT Online的DrawCall统计疑问
  • ToLua释放Asset类型内存的问题
  • 编辑器下SceneCulling如何关闭
  • 如何释放协程+UnityWebRequest下载Bundle时分配的内存

内存管理

Q:在UWA测试报告中显示我们项目PSS峰值偏高为1020MB,Reserved为450MB,是两倍多,有什么办法优化PSS吗?

我通过尝试通过adb shell dumpsys meminfo获取memory信息如下:

然后通过Android Device Monitor成功dump Java Heap详情文件hrpof文件,并且成功优化这一部分内存占用。

然而我试图通过DDMS来 dump Native Heap,却没有成功。请教各路神仙有过native heap优化的方案吗?

A:从你的dumpsys meminfo的信息来看,其实内存占用还不算太高;如果说峰值达到了1024MB,那么你就需要观察到底哪一块的内存占用较大。一般而言,这个时候可能你的native已经500MB了。优化native部分,一般有几个方向可以着手。

首先,看看ShaderLab的大小,一般而言PSS显示的部分会比你看到的ShaderLab大一倍以上,比如40MB的ShaderLab,PSS可能就有超100MB了;

其次,观察你的GameObject数量,一般而言减少几千个GameObject,你的PSS可能会减少上百兆;

再者,减少你Mono的占用,这个相信很多人都懂,优化Mono,你的Native也会减少;

再有,就是序列化这一块的东西,当然这个其实跟资源管理相关。

这一块,可以参考下UWA学堂上面的一篇文章《深度剖析PersistentManager.Remapper内存占用》 ;如果有使用Lua,它有些内存的占用也会在Native这块体现;还有就是从你的Graphics这块来看也相对的偏高一点点,可以看看资源这块是否有优化空间。以上也仅仅是优化内存的一部分,具体的问题可能需要更深入的分析才能了解。

感谢李星@UWA问答社区提供了回答,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/5d3ac2df70a3811865f79427


DrawCall

Q:UWA GOT Online这个DrawCall统计感觉有些偏高,我在Profiler里也很难看到有超过300的时候,试了在非常多野怪中战斗都不超过300,请问这个统计的来源是什么呢?

A:这个问题本质上和这里提的问题是一致:https://answer.uwa4d.com/question/5d313e6970a3811865f79369

因为GOT Online统计的DrawCall和“SnapdragonProfiler抓出来的DrawCall”是一样的,都是glDraw*的次数。所以这个数值,理论上对应的是Profiler中Rendering面板里的DrawCall数值,而不是Total Batches。

在Unity里,TotalBatches和DrawCall数不统一的情况,目前已知比较常见的有:

1、部分版本的Static Batching的实现是不合并Index Buffer的,那么一个Batch相当于是切一次材质,然后连续调用多次glDraw*;

2、部分版本的粒子系统的DrawCall合并:https://answer.uwa4d.com/question/5d0372f1d66a804458d3b4dc

该回答由UWA提供,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/5d3a848d70a3811865f79420


Lua

Q:在不用Lua的时候,C#加载一个Texture,将引用Texture的变量置空,用Resources.UnloadUnusedAssets就能把内存释放掉。

但是ToLua,会在ObjectTranslator有一个引用,这时如果C#变量置空了,ObjectTranslator中的引用还在,这个内存就释放不掉了,然后在ObjectTranslator并不知道这个Texture已经没用了,有什么方法可以释放这个内存吗?

打断点看,Texture里有个m_CachedPtr,可以看到用Resources.UnloadAsset后,会变成0x0,但是这个属性取不到,还有什么别的标记可以知道这个资源有没有被引用吗?

A1:C#释放的时候通知一下Lua也释放就可以了,类似于注册/反注册这类的操作。
感谢李星@UWA问答社区提供了回答

A2:正确操作:Lua中找到此引用,设置成nil,再Lua GC一下。
曲线救国:Texture传入Lua之后,将Texture与被赋值的component绑定。若component==null,则同时把objecttranslator里的Texture引用释放掉。
感谢hy@UWA问答社区提供了回答

A3:Unity很多内置继承自UnityEngine.Object的对象如Texture、Component等,有C#和C++ 两部分内容。

以Texture为例,C#这边就是一个Wrapper,真正的纹理数据在C++ 那边。当主动执行Resources.Unlaod之后,C++ 部分被销毁,但是C#部分由于被static容器引用没有得到释放,就会造成泄露。

查询泄露简单的办法就是在ObjectTranslator中找到相关容器进行遍历,如果发现:

if(obj == null && (obj as System.Object != null))

那么就可以认为C++层已经被销毁,但是C#还在残留,也就是泄露了。原理参考:UnityEngine.Object里的迷之null
感谢张迪@UWA问答社区提供了回答

A4:楼上的问答都很赞!这里补充一下,关于题主提到的ObjectTranslator引用了C#置空对象的问题,可以使用GOT Online的Lua报告Mono对象引用功能来检查:

具体可以查看我们的Demo报告功能介绍

该回答由UWA提供,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/5d42828370a3811865f79509


Culling

Q:我把所有摄像机的Occlusion Culling都关掉了,但是一进场景就会出现这问题,发生频率非常高,不知道还有哪里设置了,这个可以完全关掉的。在安卓上没有出现过这问题。

A:在Unity的固定流水线中,Culling是关不掉的,你只能通过尽可能去查看到底是哪些内容让sceneculling较高,sceneculling下还有其他的子函数,可以看看都是哪些子函数在占据耗时。我们在驻场优化时碰到过非常多类似的问题,有些是PrepareSceneNodes的问题,有些是LOD计算的问题,有些是Occlusion Culling的问题,有些是CullAllVisibleLights的问题,还有WaitForJob的问题等等,这些没有什么统一的优化方法,需要case by case的进行对比、测试、分析和解决。所以,建议题主根据自己项目的具体耗时,多做一些测试实验来对比分析,看看症结在哪里。

我们也将我们之前遇到过的各种案例分析进行了一个总结:

  • 关注场景中GameObject数量的影响;
  • 关注Shadowmap对于Culling的影响;
  • 关注LOD Group对于Culling的影响;
  • 关注OnWillRenderObject对于Culling的影响。

我们将这些分析结合具体案例在UWA Day 2018的《Unity引擎移动游戏性能优化全解析》进行了详细总结,题主有兴趣可以去查看了解。

下图为UWA报告中一些可以看到的问题:

PrepareSceneNodes问题

WaitForJob问题

LOD计算问题

该回答由UWA提供,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/5d3bc29a032d3c186a8e73eb


WebRequest

Q:下载步骤如下:

1、使用协程+ UnityWebRequest下载Bundle;

2、使用BinaryWriter将UnityWebRequest.downloadHandler.data的数据写入机器;

3、调用UnityWebRequest的Dispose函数;

4、调用Resources.UnloadUnusedAssets(),UnityWebRequest.ClearCookieCache() 和 System.GC.Collect();

下载每一个Bundle都会执行如上流程,但是Profiler调试真机发现,分配的内存无法释放,会把Mono内存越撑越大。求教哪位大大遇到过这种情况?

A1:楼主可以尝试从这两块分析造成Mono越来越大的原因:

1、检查自身代码,看是否还存在对这一块内存资源的引用,或者可能你又用其他对象对它保存了;按理说参照文档上面使用using语句即可,从楼主描述来看似乎没有使用using语句,而是手动dispose,这个时候最好把你的request置空,再看看是否还存在不断增长。

2、是否使用的是Mono方式而非IL2CPP的方式,如果是这种情况,那么是否有内存占用较大的Bundle呢,因为Mono的占用是只升不降的,而IL2CPP的峰值则是可以降的,楼主可以在Mono增长从过程中看看,used total与reserved total曲线之间的关系,如果需要的内存加上used mono的内存大于reserved mono的内存,那么Mono肯定是会涨的。
感谢李星@UWA问答社区提供了回答

A2:题主可以尝试自己实现一个DownloadHandlerScript。
原因参考这篇博客《Avoiding Out of Memory Crashes on Mobile》

感谢张迪@UWA问答社区提供了回答,欢迎大家转至社区交流:
https://answer.uwa4d.com/question/5d443ae927d7e5590f6cd84b

封面图来源于网络


今天的分享就到这里。当然,生有涯而知无涯。在漫漫的开发周期中,您看到的这些问题也许都只是冰山一角,我们早已在UWA问答网站上准备了更多的技术话题等你一起来探索和分享。欢迎热爱进步的你加入,也许你的方法恰能解别人的燃眉之急;而他山之“石”,也能攻你之“玉”。

官网:www.uwa4d.com
官方技术博客:blog.uwa4d.com
官方问答社区:answer.uwa4d.com
UWA学堂:edu.uwa4d.com
官方技术QQ群:793972859(原群已满员)