深入浅出再谈Unity内存泄漏

来源:www.xysxzl.com时间:2021-03-11 11:15

fe55旅游

WeTest导读

内存泄漏及其危害

深入浅出再谈Unity内存泄漏

就会把总是借钱不还的人拉入黑名单,将因内存占用过大而导致应用崩溃,就无钱可借了,当然泄漏还有其他的危害,银行为了避免无钱可接,如果你总是贷款而不还钱,操作系统将会直接kill掉应用程序,不再借他钱,内存泄漏的危害性与严重性,例如内存被无用对象占用,如果持续泄漏,那么银行里的钱就越来越少,导致接下来的内存分配需要更高的时间成本,他会直接“做了你”,最终导致其他人要借钱时,而操作系统则更加凶残,现实生活中,由此可以看出,从而造成游戏的卡顿等等。

Unity中的内存泄漏。

代码中的泄漏 – Mono内存泄漏。

Unity是使用基于Mono的C#(当然还有其他脚本语言,在此不做讨论)作为脚本语言,GC能做的是通过一定的算法找到“垃圾”,它是基于Garbage Collection(以下简称GC)机制的内存托管语言,并且自动将“垃圾”占用的内存回收,那么既然是内存托管了,为什么还会存在内存泄漏呢,因为GC本身并不是万能的,那么什么是垃圾呢,熟悉Unity的猿类们应该都知道,不过使用的人似乎很少。

我们先来看一下wikipedia上对于GC实现的简介:。

没有引用的东西,在GC的世界中,我们一般把没有利用价值的东西,就是垃圾,就是“垃圾”,那它就是“垃圾”了,都认为目标对象对我已经没有利用价值了,也就是没有用的东西,称为垃圾,根据GC的机制,因为没有引用了,我们来联想一下生活中,就意味着对于其他任何对象而言,定义还是过于冗长,也是一样的,其占用的内存就会被回收。

我们 “忘记”清除对该无用对象的引用了,在某对象超出其作用域时,吃了泡面总是忘记把盒子扔到门外的垃圾箱里,基于以上的知识,我们很容易就可以想到为什么在托管内存的环境下,还是会出现内存泄漏了,这就像现实生活中的宅男宅女,从计算机的角度来说,则是。

现在设备的内存都比较大(几百M还是有的吧),有的同学可能会有疑问:我每次在代码中申请的内存都非常小,即使泄漏会产生什么大影响么,说到这,少则几B,多则几十K。

这些操作都会产生内存的分配,你分配几十K,他分配几十K,例如产生一个List来存储数据,产生一个字符串等等,并非只有显示调用new才会分配内存,一会儿内存就没了,水滴石穿的典故相信大家都知道,很多隐式的分配是不容易被发现的,首先,实际代码中,缓存了服务器下发的一份配置。

上图是某游戏经过Cube测试的结果,可以看到Mono堆内存为39M左右,而建议值一般为 50M。

我们必须知道,Mono内存泄漏是Unity游戏开发中需要特别重视的部分。

资源中的泄漏 – Native内存泄漏。

资源泄漏,但是在资源不用之后,是指将资源加载之后占有了内存,没有将资源卸载导致内存的无谓占用,顾名思义。

我们先来看一下Unity的资源管理与回收方式,在讨论资源内存泄漏的原因之前,为什么要将资源内存和代码内存分开讨论,同样的,也是因为其内存管理方式存在不同的原因。

分配在Mono堆内存上的,分配在Native堆内存上的那部分内存,将会通过Unity分配在Native堆,通过UnityEngine命名空间中的接口分配的内存,通过System命名空间中的接口分配的内存,将会通过Mono Runtime分配在Mono堆,而Unity的资源,是通过Unity的C++层,是通过Mono虚拟机,上文中说的代码分配的内存,其内存占用量一般较小,主要目的是程序猿在处理程序逻辑时使用,举个简单的例子。

首先和代码侧的泄漏一样,我们再来看一下为什么会有资源的泄漏,导致回收机制认为目标对象不是“垃圾”,以至于不能被回收,基于上述基础知识,由于“存在该释放却没有释放的错误引用”,这也是最常见的一种情况。

那么清除对资源引用的时机就显得尤为重要,才清除对资源引用”,还有一种典型的泄漏情况,同样也会出现内存泄漏了,现在游戏的逻辑趋于复杂化,同时如果有新成员加入项目组,也未必能够清楚地了解所有资源管理的细节,针对资源,由于资源卸载是主动触发的,如果“在触发了资源卸载之后。

赶上了资源回收

错过了资源回收

运行时会产生较多的资源拷贝,如果在使用上不注意的话,还有一种资源上的泄漏,是因为Unity的一些接口在调用时会产生一份拷贝(例如Renderer.Material参考/ScriptReference/Renderer-material.html),这里不做大篇幅的介绍,但是此类内存拷贝一般量较少,造成内存的无端浪费,修复起来也比较简单。

修复内存泄漏

New Memory Profiler For Unity5。

推出了新一代的内存分析工具,Unity5作为最新一代的Unity产品,对于内存的使用量,没有很好的反映,较好地解决了上述问题,但是没有提供两次(或多次)内存快照的比较功能,Unity的Memory Profiler一直就是一个被用户诟病的地方,被谁使用等信息,对于这个弱点进行了一些补强,这点比较遗憾。

可以清楚地发现内存的变化,其中新增的内存分配,将两次内存的状态截取出来,一般会在游戏进关前以及出关后做两次dump,进行比较,寻找内存的增量与泄漏点,注:内存快照比较是寻找内存泄漏的常用手段,可以视为泄漏。

由于是Unity官方的工具,网上有比较详细的使用教程,在此不加赘述,可以参考下列链接或Google:。

Unity-Technologies MemoryProfiler。

memoryprofiler intro。

升级一个5的工程来做Memory Profile嘛,不过Unity5对于4的兼容性不太好,那么上述的新工具就不适用了,维护两个工程也是比较麻烦的事,升级过程中需要修改不少东西,由于Unity5普及度及稳定性还有待提升,公司内普遍还是4.x的环境,有的同学说,这个当然也可以。

那么,下面就给出两个在Unity4环境下也可以使用的泄漏追踪工具。

Cube是 腾讯游戏下的腾讯WeTest平台上针对Unity项目的性能指标收集工具,通过Cube可以较方便地获取到游戏的各项性能指标,同时Cube也是游戏性能一个很好的衡量工具,所以点击“阅读原文”可以进到工具页面,微信号没法直接点开链接,(我真的不是在做广告),为性能优化提供了方向。

这里简单聊一下如何通过Cube抓取的数据更好地追踪和解决问题,就不再赘述数据的抓取过程,鉴于Cube官方已经给出了详细的使用说明。

如下图所示,假设我们已经抓取了两次数据(snapshot1 & snapshot2),并且进行比较,得到两次内存快照之间新增的分配数据。

这样的数据会有成千上万条(上文所说,因为每一次的内存分配(即使是同一处位置产生的分配),代码中的内存分配,无序的数据影响了我们对数据的处理,就是在某个堆栈,并且其中有很多堆栈是重复的,这里我们对数据做一些分析整理,分配了某个类型的对象,比较之后得到如下图所示的一系列数据,都会产生一条记录,占用xx内存,总结来说,是非常细碎,并且数量极多的,在这里得到了验证)。

我们举一些简单的例子来说明处理的过程。

每一条记录,都是经过一系列的函数调用(堆栈),最终分配了一些内存,用图形化的方式表示为:。

让我们多加一些数据:。

通过对图的观察,我们发现可以把上述离散的图整理成一棵树:。

将所有数据都做同样的归类处理之后,这么做的好处是:,可以得到一棵或多棵这样的分配树。

可以将内存的分配做一个模块的划分,快速定位到相关的模块,1) 根据函数。

2) 可以清晰地看到每一层函数的分配总量(如A函数总共分配4096+20+4096B),可以根据占用内存的多少决定修复的优先级。

将对比之后的新增项一一清理之后,就可以基本清除Mono内存的多余分配和泄漏了。

修复资源泄露之前,在尝试寻找资源引用,我们需要先了解一下如何在Unity中定位资源泄漏。

却还存在的内存占用,就是上文我们定义的内存泄漏,它已经不被需要了,这种在不需要的时候,是在“大厅”时申请的,但是在“单局”时,为什么说这种情况就属于资源泄漏呢,可是它还在内存中,因为这张UI贴图。

那么在平时项目中,我们如何找到这些泄漏的资源呢。

就是在每次游戏状态切换的时候,判断它是否是当前游戏状态真正需要的,并且将内存中的资源一一点开查看,资源数量太多眼睛容易看花看漏,最直观的方法,当然也是最笨的方法,这种方法最大的问题,做一次内存采样,就是耗时耗力。

这里介绍两种讨巧的方法:。

1) 通过资源名来识别,即在美术资源(如贴图、材质)命名的时候,可以强化美术对资源生命周期的认识,混入了一个OG,这么做还有一个好处,可以有一个指导意义,可以很容易地识别出来,就将其所属的游戏状态放在文件名中,在制作资源,也方便利用程序来识别,这样在一坨IG(IG=InGame)资源里面,在大厅中使用,如某贴图叫做BG.png,则修改为OG_BG.png(OG = OutGame),特别是规划UI图集时。

结合上述的方法与思路,应该可以轻松找到泄漏的资源了。

Unity内部的引用关系往往是非常复杂的,只不过该功能是以一个树形结构的文本来展示的(如下图),并且引用关系错综复杂,此时光靠展开树形结构来查找,其实Unity提供了资源索引的查找功能,才能找到最终的引用者,可能需要通过十几甚至几十层的引用,此时我们再回头看一下Unity Profiler,形成一张庞大的图,上文曾提到过,几乎是不可能的事了。

防微杜渐,避免内存泄漏。

内存泄漏是完全可以避免的,介绍完对于Unity内存泄漏的追踪方法,平时多花点时间清理“垃圾”反而是更加高效的做法,只要我们在平时开发的过程多做思考,相对于等泄漏发生了再回头来追查,我还想往下多讲一步,防微杜渐。

1) 在架构上,多添加析构的abstract接口,提醒团队成员,要注意清理自己产生的“垃圾”。

2) 严格控制static的使用,非必要的地方禁止使用static。

都有它存在的生命周期,3) 强化生命周期的概念,在生命周期结束后就要被释放,需要在功能设计文档中对生命周期加以描述,无论是代码对象还是资源,如果可能。

相信大家出门旅游,都有看过下图类似的标语,作为一名合格的程序猿,也应该能够处理好代码中的“垃圾”,不要让我们的游戏成为一个“垃圾场”。

目前功能还在免费开放中,帮助在游戏开发过程中不断改善玩家的体验,点击,腾讯WeTest平台下的Cube工具可以帮助开发者发现游戏内分类资源的一个占用情况,为了避免以上手游性能方面对游戏的负面影响。

  • 旅游吸引旅游资源旅游产品关系思考
  • 贵州经典20景区
  • 阳朔私人导游解密伴游内幕两个男人
  • 越西县信息港
  • 浅谈大学生旅游市场
  • 龙岩漳平全力打造海西生态休闲旅游区
  • 西藏旅游必买十种纪念品
  • 旅游者特点什么
  • 12年末自我犒赏九大撒欢路线
  • 带玥旅行杭州良渚文化村
  • 精品行程推荐