




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、Unity WebGL内存详解:Unity堆Unity WebGL内存详解一文对比了WebGL及其它平台内存的工作方式。我们给出的建议是Unity堆应越小越好,同时也强调了一个事实,即浏览器中还存在其他类型的内存开销。本文将深入探讨Unity堆,并根据实际数据来减少Unity堆的大小,而不再是通过不停地调试和试错来达到这一目的。下面就来看看Unity堆的定义、原理以及如何进行Unity堆内存分析。Unity堆是什么首先要明白,Unity堆和浏览器堆是不同的概念。Unity堆实际上只是浏览器堆中的一块内存。这方面的讲解可在之前的WebGL内存详解中查看。大体上说,所谓堆就是一块用于动态分配的内存
2、,允许应用程序使用malloc/free或new/delete对内存进行操作。Unity有自己的内存分配系统,以便提高内存的利用率,同时更加方便地进行分析与调试。但在底层仍然使用malloc/free。在Unity WebGL中,将含有所有运行时Unity引擎对象的这块内存称为Unity堆。Unity堆中的内存分配是通过dlmalloc完成的。在主机平台上,这个堆的大小由硬件规格和操作系统保留内存的大小所决定,因此应用程序应保证其申请的内存不会超过运行时可用内存。在WebGL平台上同理,我们需要预先定义Unity堆的大小(即在应用程序构造时)。这就是说一旦初始化,Unity堆的大小就不会再发生
3、变化。Unity堆中有什么在Unity WebGL平台中,将Unity堆分配类型分类如下:·静态内存区域栈动态内存未分配内存·被分配的第一块区域用于栈和存放所有静态对象。栈的大小通常是5MB,而静态区域的大小取决于编译的代码,即通常受Unity和工程版本所影响。上面这些区域分配好之后,剩余的所有内存即可供运行时动态内存分配使用。当代码开始执行后,动态区域就会占据越来越多的Unity堆空间。如果这片区域占据的空间过多,最终就会导致没有内存可供Unity使用。随着时间的推移,即便会有一些对象被释放或者其他对象的内存再分配,动态内存区的大小是不会减少的,因为没有对应的压缩机制。而
4、且这类操作会使动态内存区产生碎片。所以要知道,内存中是会有碎片产生的。那托管内存哪去了?动态区域中有一个或多个运行时托管堆,程序创建的所有对象都在其中。因此,托管堆是Unity堆的一部分,而Unity堆又是浏览器JS VM堆的一部分。听起来可能有些复杂,如果看过盗梦空间或黑客帝国,那这里就好理解多了,一个堆在另一个堆中,另一个堆又在另外一个堆中,以此类推托管内存所有脚本对象都存放于此。之所以叫它“托管”是因为每当一个对象不再被引用时,垃圾回收器(Boehm)就会自动回收这部分内存。首先需要了解的一个重点是:托管内存是从Unity堆中分配的 (或从其他平台上的操作系统分配)。其次,这部
5、分内存不会再归还给操作系统,因此托管堆的大小只增不减。实际上,当一个对象被回收后,它原本占用的内存仍旧被保存在托管堆中以供将来使用。就Unity WebGL而言,当我们说“内存不会被归还给操作系统”时,实际上说的是这部分内存不会再归还给Unity堆中的可用内存块池。还有一点需要强调的是,与Unity堆不同(Unity堆是单个一整块内存),Boehm垃圾回收器有分配多重缓存的能力。另外,每一块缓存都可以按需被分割为更小的块。不过当创建新的脚本对象时,需要一块足够容纳这个对象的毗邻内存空间,如果Boehm垃圾回收器托管的可用块不足以满足需求,则会创建一个新的内存块(从Unity堆中划取)。更多关于
6、托管内存的信息,请查阅Unity手册。1托管内存用尽后会如何?如果Boehm垃圾回收器没能找到用于创建新对象的空闲内存,则从Unity堆请求分配失败,Unity WebGL将停止执行,同时抛出内存不足的错误并建议增加WebGLMemorySize的大小。2System.GC.Collect无法用于WebGL吗?Unity WebGL平台上调用GC.Collect()是没有效果的。因为调用栈在不为空的时候是无法进行垃圾回收操作的。更多有关该限制的内容可查阅Unity手册。这时,Unity WebGL会在每帧开始时尝试进行一些垃圾回收操作。之后在载入新场景时,系统会进行一次完全垃圾回收操作。3Sy
7、stem.GC.GetTotalMemory具体做什么?在Unity WebGL平台上,该函数的作用与在其他平台上比是相同的,同时也提供了垃圾回收异常机制:System.GC.GetTotalMemory() 返回当前使用的所有托管内存,正如Profiler.GetMonoUsedSize()一样。如需了解托管堆的总大小(已使用+空闲),可以使用Profiler.GetMonoHeapSize()。4如何在托管堆中保留一定数量的内存?如果曾用过C+ std容器(例如string,vector等等),应该已经了解在向容器中追加或插入新元素时,它们的大小会发生变化。在需要将使用内存控制在
8、一定范围内的游戏和一些其他应用程序中,这可能是个问题,不过可以使用预留内存方法(例如:std:string:reserve, std:vector:reserve)来解决这一问题。与C+ std容器不同,Unity中没有为托管堆提供类似的内存保留API。不过此前也曾提到,可以另辟蹊径来达成这一点。假设已经预先知道程序内容的托管堆占用大小,就可以预先创建一个大小相同的数组,然后手动运行垃圾回收器。这样就“隐式”地为托管堆保留了这一块内存,从而托管堆也不再需要扩容了。听着是个相当不错的思路,但正如我们之前提到的,调用GC.Collect()函数不会有任何效果,且完全垃圾回收机制仅在场景载
9、入时被激活。当然,这个问题还是比较容易解决的,可以设置一个预加载场景,其中仅有一个游戏对象,将分配数组的脚本附加在对象上。然后,将这个场景设置为工程的第一个场景。现在万事俱备只欠东风,我们需要知道预分配托管堆的大小:可供内容从头至尾运行,然后使用Profiler.GetMonoHeapSize()函数获取保留内存的总大小。最后记住一点,使用该方法的代价是,程序的托管内存永远都是最大值。设置Unity堆大小解释过Unity堆与内存管理脚本之后,回到最开始的问题:选择Unity堆大小的最佳策略是什么?基本思路是要知道运行内容所需的最大内存占用量,然后将WebGLMemorySize设置为一个稍大的
10、值(下一个16的倍数)。具体做法是完整测试WebGL程序的所有内容,记录所占内存的峰值大小。然后将最终大小再稍加扩大以防万一,将其调整为16MB的倍数。好在Profiler API提供了获取总内存大小(Total Reserved Memory)的函数。Profiler.GetTotalReservedMemory(),对应Unity Profiler窗口中的ReservedTotal:但是这种方法存在两个问题。第一个问题和内存峰值有关:如果临时内存的分配和释放发生在同一帧时,这部分开销就不会被计算在ReservedTotal中。第二个问题是Unity Profiler不会记录所有的内存分配。
11、未跟踪的内存分配从Profiler中获取的信息可以发挥巨大的作用,然而还需要考虑到一些事情:显而易见,Profiler会告诉您它知道的一切,但它无法告诉您它不知道的东西!Unity之所以能够追踪内存的使用情况,是因为内存的分配都是通过MemoryManager:Allocate()完成的,而该函数会存储有关内存分配名称和大小的额外信息。不过出于某些原因,还有一些其他的内存分配操作不会被追踪,因此如果想要确切地知道Unity堆里占用了多少内存,这一点就会成为问题。这通常是因为某些内部子系统和第三方库在操作内存时使用的是malloc/free函数,而不是Unity自身的MemoryManager。
12、除此之外,用户的插件也有可能产生这样的情况,例如C和C+代码中的mallc()函数,或JavaScript中的_malloc()函数(例如JS样例插件中的StringReturnValueFunction函数),或者是文件的写入操作(这也会导致内存的写入)。为了能给出清晰的概念来表明未能追踪的内存究竟有多大,就拿Unity 5.5中的一个简易工程来说,这个数字大概是7MB。好消息是,我们正在着手解决这个问题,将来这个数字只会减少。如何知道内存的准确用量实际上还是有方法的。我们再次回顾最开始的图片,很容易就能发现总内存占用(Total Reserved Memory) = 静态内存 + 栈 +
13、动态内存。幸运的是,我们能够实时地获取到这些内存区域的大小,使用emscripten生成的变量和常量即可。之后仅需存入jslib文件并存储在工程里,然后创建对应的C#代码即可。请点击【阅读原文】查看代码。使用这种方式还有一个好处,与Profiler API不同,这个插件可以在发布版本中使用。需要注意的是,上述的jslib代码依赖于emscripten生成的JS代码,因此在将来的Unity版本中,这个插件可能需要更新。不过既然已经发现了这个问题,我们可能会为其添加Unity WebGL专用API来避免这样的问题。如何分析Unity堆的数据首先可以使用Unity Memory Profiler内存分析器,该分析器可以提供内存数据的总览,以及所有内存分配类型的详细信息。如果需要排查内存泄露,可以参考CPU分析器中的GC Alloc一栏。这一栏可以清楚表明在某一帧分配了多少内存。顺便一提,如果在使用分析器的过程中遇到问题,有可能是5.3中的bug。我们已经在5.3.6 Patch 8中修复了这个问题。如果想获得更底层的数据,可以尝试新的内存分析器(Unity 5.3中提供):Memory Profiler是个非常实用的工具,但还是要注意一点:它只适用于il2cpp(当
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年数字货币与金融监管考试题及答案
- 2025年电子商务专业相关考试题及答案
- 2025年国际关系理论与实践考试试题及答案
- 2025年全国社会科学专硕入学考试试卷及答案
- 2025年民法与商法相关考题及答案
- 监理合同变更协议书
- 宠物医疗技术专项考核试题及答案
- 浙江省信息产业厅主要工作情况及明年工作设
- 2025年进口啤酒项目合作计划书
- 水产养殖与生态农业综合开发股权合作协议
- 2025年四川省成都市青羊区中考二诊化学试题(原卷版+解析版)
- 2025年华侨港澳台生联招考试高考地理试卷试题(含答案详解)
- 【MOOC】软件质量保证-西安交通大学 中国大学慕课MOOC答案
- MSOP(测量标准作业规范)测量SOP
- 稻谷加工毕业设计日加工籼稻400吨免淘洗大米生产线设计
- 因式分解—完全平方公式
- 社会保险申请表
- 2020年精品收藏微型企业创业扶持申请书全套表格
- (完整版)高速公路拌合站设置规划方案
- 战略与战略管理ppt课件
- 《全国英语等级考试》
评论
0/150
提交评论