内存管理是程序开发的核心问题,而资源的使用又与内存息息相关,因此本章想要梳理整个开发流程中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。

Unity内存与资源管理相关推荐

  1. 深入浅出再谈Unity内存泄漏

    作者:Arthuryu,腾讯高级开发工程师 著作权归作者所有.商业转载请联系腾讯WeTest获得授权,非商业转载请注明出处. WeTest导读 本文通过对内存泄漏(what)及其危害性(why)的介绍 ...

  2. Unity中的资源管理-几种常见的序列化方式

    本文分享Unity中的资源管理-几种常见的序列化方式 在网游客户端的开发中, 大部分数据只需要从服务器获取数据之后存放在内存中, 但是仍然有一些数据需要做序列化, 并持久化存放在客户端本地. 比如用户 ...

  3. Unity 项目中资源管理(续)

    转载自:https://zhuanlan.zhihu.com/p/28324190 上次和大家分享了Unity项目中的资源管理主要讲资源配置以及资源配置工具,Unity资源配置在资源管理中处于基础地位 ...

  4. Unity中的资源管理-AssetBundle(1)

    本文分享Unity中的资源管理-AssetBundle(1) 在上一篇文章中, 我们简单介绍了Unity中的资源和基本的使用, 今天我们详细介绍下使用AssetBundle来管理资源. AssetBu ...

  5. Unity中的资源管理-引用计数

    本文分享Unity中的资源管理-引用计数 在前面的文章中, 我们一起学习了对象池的基本原理和几种实现, 今天和大家继续聊聊另一个资源管理中比较重要的技术: 引用计数. GC的基础知识 GC(Garba ...

  6. UNITY 内存问题资料收集

    UNITY 内存问题资料收集 1,https://blog.csdn.net/wetest_tencent/article/details/52130703 2,http://blog.51cto.c ...

  7. Unity运行时刻资源管理

    原地址:http://www.cnblogs.com/88999660/archive/2013/04/03/2998157.html Unity运行时刻资源管理 ------------------ ...

  8. Unity内存管理你应该知道的底层原理

    本文首发公众号洪流学堂.洪流学堂,让你快人几步. 本文主要是Unity官方川哥的视频<浅谈Unity内存管理>的笔记及相关知识点补充,如果有时间强烈建议学习原视频: https://www ...

  9. 【Unity】Unity内存管理与优化(一)内存域、堆栈、垃圾回收、内存泄漏、内存碎片

    文章目录 Unity内存 内存域 - 托管域 - 本地域 - 外部库 - 跨桥操作 堆和栈 - 栈 - 堆 - 堆栈的使用 垃圾回收 - Mono内存分配过程 - 内存泄漏 - 内存碎片 - 运行时垃 ...

最新文章

  1. 利用JS获取IE客户端IP及MAC的实现
  2. .net一个函数要用另一个函数的值_【195期】MySQL中的条件判断函数 CASE WHEN、IF、IFNULL你会用吗?...
  3. c# datagridview 相关操作。
  4. java内部类选择题_java内部类详解(附相关面试题)
  5. arcgis中的python字符串比较
  6. sql调优的几种方式_「数据库调优」屡试不爽的面试连环combo
  7. java垃圾收集方法_java几种垃圾收集方法和垃圾收集器
  8. 关于LineChart线条颜色和粗细的问题
  9. AI加持,计算机要拥有嗅觉了;GPU终于可用于Google Compute Engine | AI开发者头条
  10. 联想第三季:PC+时代的航母启航?
  11. Rust: 如何在Windows下Atom中配置Rust环境?
  12. SANGFOR SCSA——网络基础(下)
  13. 通达信指标大全_选股指标:通达信指标大全,筹码起爆最佳的信号抄底位置
  14. iphone 与 PC端电脑投屏设置
  15. setw和width
  16. 迷宫(Maze)项目实现
  17. 利用D盘内存给C盘扩容
  18. android p cts camera测试 android.hardware.camera2.cts.CaptureRequestTest#testEdgeModeControl fail
  19. cc2530 按键中断实验——按键控制LED灯的亮灭
  20. 【转】偷偷告诉你快速提高app下载量和安装量

热门文章

  1. 2022年全国职业技能大赛网络安全竞赛试题B模块自己解析思路(7)
  2. Openssh 8.8制作CentOS 7.6下的rpm包
  3. 用 QGIS 画矢量交通路线图
  4. 用计算机怎么弹出凉凉这首歌,凉凉歌词的意思是什么 凉凉歌曲表达的意思
  5. IMX6ULL 修改内核启动LOGO
  6. 【esp8266】小黑板ESP8266无线wifi SoC方案连接机智云最详细图文教程
  7. 十三水牌型 图片_十三水,得玩法到底有多少种!
  8. Ubuntu22.04TLS插入3.5mm耳机没有声音
  9. 使用EasyExcel导入表格实现xlsx文件批量插入-----linux的mysql
  10. Oracle DataGuard备机出现ORA-00600 [2619]错误的处理思路