Unity游戏Mono内存管理及泄漏
更新时间:2023-04-18 03:24:01 阅读量: 实用文档 文档下载
内存是手游的硬伤——Unity游戏Mono内存管理及泄漏
WeTest导读
内存是游戏的硬伤,如果没有做好内存的管理问题,游戏极有可能会出现卡顿,闪退等影响用户体验的现象。本文介绍了在腾讯游戏在Unity游戏开发过程中常见的Mono内存管理问题,并介绍了一系列解决的策略和方法,无论是游戏还是VR应用,内存管理都是其研发阶段的重中之重。
什么是Mono内存
对于目前绝大多数基于Unity引擎开发的项目而言,其托管堆内存是由Mono分配和管理的。“托管”的本意是Mono可以自动地改变堆的大小来适应你所需要的内存,并且适时地调用垃圾回收(Garbage Collection)操作来释放已经不需要的内存,从而降低开发人员在代码内存管理方面的门槛。
Unity游戏在运行时的内存占用情况可以用下图表示:
目前绝大部分Unity游戏逻辑代码所使用的语言为C#,C#代码所占用的内存又称为mono内存,这是因为Unity是通过mono来跨平台解析并运行C#代码的,在Android 系统上,游戏的lib目录下存在的文件,就是mono在Android系统上的实现。C#代码通过mono解析执行,所需要的内存自然也是由mono来进行分配管理,下面就介绍一下mono的内存管理策略以及内存泄漏分析。
Mono内存管理策略
Mono通过垃圾回收机制(Garbage Collect,简称GC)对内存进行管理。Mono内存分为两部分,已用内存(used)和堆内存(heap),已用内存指的是mono实际需要使用的内存,堆内存指的是mono向操作系统申请的内存,两者的差值就是mono的空闲内存。当mono需要分配内存时,会先查看空闲内存是否足够,如果足够的话,直接在空闲内存中分配,否则mono会进行一次GC以释放更多的空闲内存,如果GC之后仍然没有足够的空闲内存,则mono会向操作系统申请内存,并扩充堆内存,具体如下图所示。
通过上文可知,GC的主要作用在于从已用内存中找出那些不再需要使用的内存,并进行释放。Mono中的GC主要有以下几个步骤:1.停止所有需要mono内存分配的线程。
2.遍历所有已用内存,找到那些不再需要使用的内存,并进行标记。
3.释放被标记的内存到空闲内存。
4.重新开始被停止的线程。
除了空闲内存不足时mono会自动调用GC外,也可以在代码中调用()手动进行GC,但是,GC本身是比较耗时的操作,而且由于GC会暂停那些需要mono内存分配的线程(C#代码创建的线程和主线程),因此无论是否在主线程中调用,GC都会导致游戏一定程度的卡顿,需要谨慎处理。另外,GC释放的内存只会留给mono使用,并不会交还给操作系统,因此mono堆内存是只增不减的。
Mono内存泄漏分析
Mono是如何判断已用内存中哪些是不再需要使用的呢?是通过引用关系的方式来进行的。Mono会跟踪每次内存分配的动作,并维护一个分配对象表,当GC的时候,以全局数据区和当前寄存器中的对象为根节点,按照引用关系进行遍历,对于遍历到的每一个对象,
将其标记为活的(alive)。
如上图所示,假设A是处于全局数据区的一个对象,那么在GC的时候将作为根节点进行遍历,由于B、C、D对象都可以由A遍历到,因此被标记为活的,E、F对象则没有被标记。注意,由于引用关系是单向的,A引用了B并不代表B也引用了A,所以遍历也只能单向进行。
由于GC以全局数据区和当前寄存器中的对象为根节点进行遍历,所以对象的被标记意味着该对象可以通过全局对象或者当前上下文访问到,而没有被标记的对象则意味着该对象无法通过任何途径访问到,即该对象“失联”了,GC最终会将所有“失联”的对象内存进行回收,上图中的E和F将会在GC过程中被回收。
既然mono已经有了完善的GC机制,那是否还会存在内存泄漏呢?答案是肯定的,只是此处的内存泄漏需要重新定义一下,我们把对象已经不再需要使用却没有被GC回收的情况称为mono内存泄漏。Mono内存泄漏会使空闲内存减少,GC频繁,mono堆不断扩充,最终导致游戏内存占用的升高。下图就是一个mono内存泄漏的例子。
解决办法
对于mono内存泄漏,一般只能通过猜测+不断修改代码测试的方法来修复问题,效率很低,腾讯Wetest平台的Cube工具提供了mono内存快照对比的功能,并包括对象分配堆栈,对象引用关系等详细信息,是定位mono内存泄漏问题的一大利器。下面结合具体的代码尝试使用Cube定位mono内存泄漏问题。
首先我们定义类A,并在A的构造函数中申请了一块int[1000]大小的内存。
接着我们定义A类型的静态变量objectA,在游戏界面上绘制一个按钮,并在按钮点击事件中给objectA赋值,此时新生成了new int[1000]对象,并由objectA引用。
使用Cube的mono内存检测功能,并在按钮按下之前和按下之后分别进行一次快照,对比两次快照,查看快照间新增对象。
可以看到,按钮按下前后新增的最大对象即为代码中生成的new int[1000]对象,并且该对象被引用的次数为1,为了查看详细的引用关系,下载快照文件snapshot2,其中有这样两行数据:
第一行说明在OnGUI函数中生成了一个A类型的对象,其指针为28,第二行说明在OnGUI()->A:.cotr()中生成了一个Int32[]类型的对象,并且该对象被指针为28的对象引用。即new int[1000]对象被objectA引用,这也是导致new int[1000]对象无法被GC回收的原因。而objectA本身是一个静态对象,是GC的根节点,因此没有对象引用。
如果需要生成的new int[1000]对象被回收怎么做呢?很简单,将设置为null,没有了objectA对其的引用,自然会被GC回收了。需要说明的是,将设置为null只是断绝了引用关系,真正对象的回收要等到GC的时候才会进行,Cube在获取内存快照的时候会首先进行一次GC,防止由于没有及时调用GC导致的误判。
游戏中大部分mono内存泄漏的情况都是由于静态对象的引用引起的,因此对于静态对象的使用需要特别注意,尽量少用静态对象,对于不再需要的对象将其引用设置为null,使其可以被GC及时回收,但是由于游戏代码过于复杂,对象间的引用关系层层嵌套,真正操作起来难度很大。可以首先使用Cube工具进行分析,根据mono内存趋势找出泄漏的具体场景,然后再使用快照对比功能进行详细分析。
工具现在提供免费使用体验地址:
关于测试报告的问题:使用帮助:
常见问题:
正在阅读:
Unity游戏Mono内存管理及泄漏04-18
根据学生辅导员的岗位特点及工作要求完善和落实-上海外国语大学06-10
网络直销平台优缺点分析和可行性07-31
食堂管理制度03-10
XX年4月网页设计专业大学生实习报告03-10
公路工程施工总结报告03-09
20109-2012年度工作总结103-10
四川师范大学附属中学数学轴对称填空选择专题练习(解析版)05-06
小猪唏哩呼噜读后感一年级03-10
- 教学能力大赛决赛获奖-教学实施报告-(完整图文版)
- 互联网+数据中心行业分析报告
- 2017上海杨浦区高三一模数学试题及答案
- 招商部差旅接待管理制度(4-25)
- 学生游玩安全注意事项
- 学生信息管理系统(文档模板供参考)
- 叉车门架有限元分析及系统设计
- 2014帮助残疾人志愿者服务情况记录
- 叶绿体中色素的提取和分离实验
- 中国食物成分表2020年最新权威完整改进版
- 推动国土资源领域生态文明建设
- 给水管道冲洗和消毒记录
- 计算机软件专业自我评价
- 高中数学必修1-5知识点归纳
- 2018-2022年中国第五代移动通信技术(5G)产业深度分析及发展前景研究报告发展趋势(目录)
- 生产车间巡查制度
- 2018版中国光热发电行业深度研究报告目录
- (通用)2019年中考数学总复习 第一章 第四节 数的开方与二次根式课件
- 2017_2018学年高中语文第二单元第4课说数课件粤教版
- 上市新药Lumateperone(卢美哌隆)合成检索总结报告
- 泄漏
- 内存
- Unity
- 管理
- 游戏
- Mono
- 2022年厦门大学财务管理与会计研究院808管理学与管理经济学之西
- 中国特色词汇翻译及旅游地名称
- 诗歌鉴赏——题目、注释在鉴赏诗歌中的作用教案
- 英语考试作文-雅思写作高分范文和详解:旅游便利产生的利与弊
- 行政法与行政诉讼法(一)00923自考本科历年真题及答案200304—201
- 在人才工作座谈会上的发言
- 牛津译林版高中英语必修三Unit 2《Language》(Grammar and usage
- 2022年北京外国语大学西葡语系448汉语写作与百科知识
- 电脑技术常见365个问题
- More media coverage is being paid to the HIV
- 【百强校】2022届福建省三明市一中高三特保班上第二次月考生物试
- 应用回归分析-第2章课后习题参考答案
- 上海某大厦机电设备安装施工组织设计_secret
- 液压传动试题(带答案)集锦
- 2022年上饶市事业单位考试计算机真题与答案解析
- 微观经济学知识点总结
- 毕业论文:我国服务贸易的现状及对策分析
- 2022-2022高一算法概念练习题及答案
- 高中化学 3.1 最简单的有机化合物 甲烷课时作业1 新人教版必修2(
- 人教版小学校本课程教材古诗300首(三年级下册)