【UE4】界面打开关闭异常闪退

第一次处理(未完全解决):

问题:

项目组上周测试的 IOS 包,打开关闭主要的角色界面,达到一定次数之后,必定闪退。
由于前不久在这个界面增加了场景的切换,以及一个镜面反射,首先认为应该是镜面反射导致。由于上周有其他优先级较高的任务,所以今天才着手解决此问题。

分析:

原先以为会是渲染相关导致的闪退,今天仔细查看 bugly 之后发现是内存堆积导致,在控制台使用Objects List 命令打印出对象列表,发现鼠标点击特效频繁构建新的 UMG (对,我写的,没用对象池)。制作了对象池之后,再观察打印打印出来的对象列表,有少量优化,但是区别不大,并且隔一会儿之后都会被回收,所以还是从其他方面着手找问题。
再次观察 bugly 追踪堆栈之后发现,问题都是由几个会调用界面初始化逻辑的方法触发,一处是打开界面,另一处是在界面中切换页签。于是从业务下手整理逻辑,发现的确有几个初始化逻辑被反复调用,优化之后切换页签闪退情况消失。打开关闭界面闪退情况依旧存在。
继续多次测试之后,发现打开关闭界面的闪退情况甚至没有上报到 bugly,只能使用 UE4 的 Device Output Log 观察日志(其实之前是我忘了还有这个,而且我的电脑不知道为什么发现不了设备,用的组长的电脑查看的)。

解决:

观察日志后,马上定位到原因:某次构建对象时,项目中的总对象数量超过了预设的最大对象数量。从报错的日志选取关键字搜索,马上得到了解决方案(日志和引用存在公司,回头补上)。 在路径为 项目名/Config/对应平台/ 的目录下找到 平台Engine.iniIOSEngine.ini,修改其中的 gc.MaxObjectsInGame,默认大小为 10w 左右,测试过 500w 和 30w 的值。500w 妥妥地不会闪退,但是不合理占用内存;30w 在多次打开关闭界面后还是闪退,最终将值定在 100 w,暂时解决该问题。

第二次处理(基本完全解决):

问题:

第一次处理方法只是临时扩大了占用内存上限,并未实际解决问题。后续项目空闲的时候重新查了一遍,基本项目得到了完善的解决。

查找:

问了隔壁组查内存问题的方法:

1、拉取内存报告
游戏运行时呼出控制台,输入:memreport
在这里插入图片描述
些许卡顿之后会在 Saved\Profiling\MemReports\ 出现该时刻对应的内存报告,其中详细列出了内存中对象、图片、模型、渲染等的数量和内存占用情况。
手机在 debug 包下可以在 Device Output Log 窗口中输入 memreport 拉取,也可以三指点击手机屏幕唤出控制台输入,报告会在手机中对应目录中。

编辑器报告路径:
在这里插入图片描述

2、查看对象列表
分析内存报告,找出数据不正常的对象,再回到游戏。

游戏运行时呼出控制台,输入:obj list
些许卡顿后可以在日志窗口看到当前存在的对象列表,这个信息实际跟内存报告中一样。

游戏运行时呼出控制台,输入:obj list class = 类型名 (蓝图类型后缀需要加上 _c)
然后可以在日志窗口看到这个类型所有对象的相关信息,找出路径不正常的对象,比如路径上有已经销毁的对象。

obj list class = CameraActor 结果:
在这里插入图片描述
3、查找对象引用
找出不正常的对象后,复制整串路径。
游戏运行时呼出控制台,输入:obj refs name = 对象完整路径
然后可以在日志窗口中看到这个对象的所有引用,有一些引用并非对象未销毁的原因,可能清理其他引用后就没有了,这个具体要根据项目分析。

对象引用日志:
在这里插入图片描述

分析

分析所有数据后,主要集中两个问题:

1、切换场景未销毁对象
切换场景后,有部分对象未销毁,即便再切换到上一场景后该部分对象也未销毁,场景中就存在了多份相同功能的对象。这也是当然的 -。- 就没有回到场景一说,实际就是进行一次场景切换。
Actor 基本不存在该情况,大概因为 Outer 的根节点在场景上吧,关于这个看心情另外再查查写一篇。
笔者项目中的这种对象基本集中在系统中处理数据的UObject,基本是由于被引用没有及时清理。

2、界面未销毁
界面关闭后没有及时销毁。关闭界面后,界面不会被马上清理,等待周期 GC 后才会清理。所以如果频繁的开关一个界面会导致没有清理的界面堆积,如果在这个界面下还放了很多其他对象,那么内存堆积的情况可想而知。
而且,笔者项目使用的是 Unlua,界面绑定的 lua 会使得界面不被添加到 GC 列表中。
重新看了 Unlua 的示例项目,也对比了通公司隔壁组的项目。示例项目中,每个界面的 lua 在析构时调用了 Release()。隔壁组的项目中,给界面写了一个模板类,在模板类的析构中调用了 Release(),另外写了一个接口给子类界面编写析构时需要执行的操作。

解决:

第 1 个问题好解决,就是找项目中的逻辑问题,然后一点一点清理,具体时间成本只能根据项目问题评估。

第 2 个问题实际解决办法就是使用 Release()。当时笔者所在项目已经创建了许多 lua 绑定的界面,而且也没有引用同一个 lua 模板。所以不管是每个 lua 都补上 Release(),还是使用 lua 模板解决对于当时的工作量都不合理。但是项目中的界面都是继承自同一个项目组自定义的 C++ 类。
所以笔者就找了 Release 对应到 C++ 的反射函数:
在这里插入图片描述
在这里插入图片描述
把上图中红框内逻辑,移到界面父类的析构函数中,和执行了 Release() 的效果是一样的。
GLuaCxt 肯定是要改动的,笔者只是心血来潮想起来补写一下这部分内容,之前改动是在上家公司操作的,具体怎么改动已经忘了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值