Unity内存与资源管理

内存管理是程序开发的核心问题,而资源的使用又与内存息息相关,因此本章想要梳理整个开发流程中Unity对于内存与资源的管理方式。

一、内存基础

1.1 基础概念

内存是暂时存放CPU中的运算数据,与硬盘等外部存储器交换的数据。在操作系统中,内存分为物理内存与虚拟内存。

CPU读取数据

CPU进行数据处理时,从内存或缓存中取出指令,放入指令寄存器,并对指令译码进行分解,进而对数据进行处理。从内存中读取数据很慢,通常CPU会将之前读取的数据缓存在多级Cache中,提升数据访问效率。因此,CPU会先从Cache中查找数据,若没有找到(Cache Miss),才会访问内存。

物理内存:物理内存可以理解为运行时内存,其对应于实际的存储硬件。

虚拟内存:管理不同进程使用的进程内部内存地址和物理内存地址的映射体系。

页:内存管理、映射中的基本单位。

内存交换(页面交换):当系统内存不足时,操作系统会创建交换文件(Swap file)或者分页文件(Paging file)记录暂时不用的物理内存页面中的数据,并将这部分数据交换到硬盘上,节省更多的物理内存。

1.2 移动设备与PC差异

  • 没有独立显卡,显存与CPU数据会存储在同一区域。
  • CPU的Cache与内存更小。因此移动端会严格限制资源大小。
  • 没有内存交换(IO速度慢、可擦写次数少)
  • IOS可以进行内存压缩:将不活跃的内存压缩到特定空间。

1.3 Android内存分布

上图可以看出运行时内存包括:方法区、堆栈以及线程内存。

Android中对内存有4种统计方式:VSS(虚拟内存)、RSS(实际物理内存:APP+所有共享内存)、PSS(APP+平均共享库内存)、USS(APP内存)。通常使用PSS来统计。

Android对进程的管理策略称为LMK(Low Memory Killer),kill优先级如下:

1.4 IOS内存分布

IOS内存分布

Clear Memory包括系统框架数据、可执行的二进制数据以及内存映射文件。Clear Memory以外的内存都是Dirty Memory。

当系统存在内存压力时,会先卸载一部分Clear Memory,当再次需要时会对其重新创建。但系统无法对Dirty Memory进行卸载,直到到达App的限制才会被终止回收。内存不足时,IOS会对Dirty Memory做压缩处理。

IOS中Resident Memory指的是App分配的物理内存。当App向系统申请内存时,虚拟内存是直接增长的。但如果申请完的内存并没有向里面写入数据,它并不会产生实际的物理内存分配。

iOS的进程管理策略称为Jetsam,GitHub有人测试了其kill进程的优先级:

Jetsam优先级

二、Unity内存与GC

从Editor与Runtime比较,在Runtime时,只有Load资源才会影响内存,而Editor模式下,为了方便开发,只要打开Unity资源就会被加载(Unity打开很慢,2019.3的Asset Pipeline 2.0做了优化)。

2.1 Mono与IL2CPP的跨平台

Mono与IL2CPP是Unity的两种跨平台解决方案,目前官方推荐使用IL2CPP。

Mono跨平台流程

Mono的跨平台方案:编译器mcs将代码编译为IL,通过Mono运行时中的编译器将IL编译成对应平台的原生码。

Unity Mono是针对每个特定平台做了处理,因此才能跨平台。若出现新的硬件平台,则需要额外造轮子,其可移植性很差,因此Unity给出了IL2CPP方案。并且IL2CPP相比Mono有一定的性能提升。

IL2CPP方案流程(来自官方文档)

编译工程时,IL2CPP将Unity Scripting API 代码编译为常规 .NET DLL(托管程序集)。将所有托管程序集转换为标准C++代码。使用本机平台编译器编译生成的C++代码和IL2CPP的运行时部分。最后将代码链接到可执行文件或 DLL,具体取决于目标平台。

IL2PP相比于Mono,最大的变化在于由JIT编译变为了AOT编译。由此带来的变化体现在:(1)开发编译时间变长;(2)无法动态生成代码;(3)应用程序体积变小;(4)应用程序启动时间短。

在开发过程中可以选择Mono,提升开发效率,发布应用时使用IL2CPP得到更好的程序性能。

2.2 Unity VM GC

当Unity VM需要分配内存时,会检查当前内存。若内存足够时,可以直接分配内存,但内存不足时会触发GC,再次检测内存。若此时内存仍然不足,VM会向操作系统申请内存。

触发GC时,VM会暂停所有线程,可能引起程序卡顿。Unity采用了BOEHM处理GC,其为标记清除法。在标记阶段通过访问根节点,并遍历到叶子节点,最终将所有存在引用的内存都标记出来,清除未被标记的托管内存。

Unity的VM以Block管理内存,当一个Block连续6次GC没有被访问到(很难触发),这块内存会被返回给系统。

由于此种处理方式没有内存压缩,并且可能出现内存黑名单,因此容易出现内存碎片。在开发过程中,要尽量减少内存的申请,申请的内存尽量保持连续,对于资源加载应该优先加载较大的资源。

2.3 Unity内存分布

Unity内存分布

Unity内存分为Native堆内存、第三方库、虚拟机以及GfxDriver。Native堆内存中存储由Unity底层管理的数据,如音频、视频、Texture、Mesh、GameObject等。第三库常见的如tolua、Wwise。虚拟机通常指C#所产生的内存。GfxDriver是指当前驱动使用的Textures、RenderTargets、Shaders和Mesh数据内存。

三、Editor Asset

Unity 中的资源工作流程(来自官方文档)

Unity中的资源通常是指3D模型、音频文件、图像等用于程序使用的数据(代码数据也是一种资源)。程序开发阶段,将外部资源导入或复制到Asset目录下,Unity会监听到所导入的数据,并生成相应的Unity默认资源。

3.1 创建asset

Unity检测到新资源(文件夹)时会向此资源分配GUID,这是Unity在内部使用的ID,用于索引资源,以便Unity移动或重命名此资源不会破坏引用关系。

Unity对导入的外部文件格式不会进行修改,对内置格式资源(prefab/material/unity等),Unity以YAML格式进行存储。

prefab在Unity中的存储格式

文件前两行是文件格式版本注释,后续以--- !u!classID &fileID对组件数据进行标记。classID是Unity为组件类型分配的id,fileID为prefab中组件实例化的局部id,由此可以进行索引。上图GameObject挂载了3个组件,所以可以看到m_Component通过fileID映射到相应的组件实例。由于EventSystem是自定义组件,由此可以看到m_Script通过guid来引用到相应的代码资源。

由于这种格式化的存储方式,在非Unity Editor情况下处理或者查找带有特定参数的资源,可以通过纯文本替换或查找处理。(需要以下设置)

以文本格式存储序列化资源

3.2 监听资源导入

Unity提供了用于监听资源导入的基类,方便开发者对资源做标准化处理,具体基类如下:

来自Unity用户手册

3.3 资源处理

Unity在导入资源后,会在Library文件夹下生成真正用于Unity处理的资源。例如,Unity 将 .png 图像文件导入为纹理时,在运行时不会使用原始的 .png 格式数据,而是创建新的图形格式,将其存储在项目的Library文件夹中。Unity的Texture类会使用此导入版本,然后Unity将其上传到GPU进行实时显示。

除了Library,Unity会在Asset文件夹相同位置生成资源的.meta文件,用以记录资源设置参数与GUID。

meta文件

在Unity 2020以前的版本可以通过guid,在Library下搜索到Unity生成的资源。在Unity 2020之后的版本,会在Library下生成ArtifactDB和SourceAssetDB文件。

SourceAssetDB包含.meta相关数据(上次修改日期、文件内容哈希、GUID和其他元数据信息),由此判断是否需要重新导入资源。

ArtifactDB包含每个源资源的导入结果的信息。每个Artifact都包含导入依赖项信息、Artifact元数据信息和 Artifact文件列表。

从上述资源加载流程来看,项目中经常遇到资源引用丢失:(1)美术资源与meta没有一起上传,导致prefab无法通过guid找到资源。(2)美术替换资源时,先删除原有资源,再导入创建新资源,导致原有的prefab引用的guid失效。

对于漏传meta的情况,可在提交版本文件时做强制检测,若Asset下新增资源必须要有对应的meta文件。对于美术资源的替换,不能在Unity Editor下直接删除文件,需要通过import替换,或者在文件夹中替换资源再打开Unity。

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值