找回密码
 立即注册
查看: 212|回复: 0

Unity内存管理机制笔记--杨沁七

[复制链接]
发表于 2022-11-19 11:20 | 显示全部楼层 |阅读模式

  • 内存管理基础

    • 物理内存

      • CPU访存速度慢:先Cache;再内存
      • Cache Miss会导致大量的内存和Cache的IO交换,浪费大量时间

        • Unity提出ECS方案来降低Cache Miss的概率







      • 台式设备和移动设备架构的差异

        • 移动设备没有独立显卡
        • 移动端数据内存和显存共享同一块内存
        • 移动设备的CPU面积小,导致缓存级数少,大小也更小







    • 虚拟内存

      • 定义:利用磁盘空间虚拟出一块逻辑内存,这一块磁盘空间被称为交换空间
      • 内存交换操作:操作系统在内存不足时,将一些不用的内存交换到硬盘上

        • 移动设备没有这个操作

          • 移动设备IO速度很慢
          • 移动设备的可存储物的可擦写次数比硬盘少很多,会减少使用寿命









      • 内存压缩:IOS中将不活跃的内存压缩起来存储到一个特定空间,以节省出物理内存空间
      • 内存寻址范围

        • 定义:CPU对于内存寻址的能力
        • 范围与Memory Controller(内存控制器)有关,与运算位数(64 or 32)无直接关系







    • 总结:在移动设备中需要更加高效的内存管理机制

      • 实际物理内存小,没有虚拟内存
      • 缓存级数少,大小也更小





  • 移动设备的内存管理

    • Android内存管理

      • 基本单位Page:4K
      • 用户态和核心态
      • 内存杀手:Low Memory Killer当内存不足时,会清理内存
      • Android应用分层
      • 内存指标

        • RSS:独占+Services
        • PSS:独占+均摊的Services
        • USS:独占







    • IOS内存管理



  • Unity内存管理

    • Unity是一个C++引擎

      • 最底层:Runtime,全是Native C++代码
      • 最上层:C#,例如Unity的Editor和部分Package
      • 中间层:Binding,将C#与C++联系起来,为C#提供API
      • 虚拟机:用于跨平台

        • Mono

          • 目标:在尽可能多的平台上能正常运行.net标准程序
          • 组成:C#编译器、CLI虚拟机和核心类别程序库
          • 工作流程
          • 首先,通过C#编译器mcs,将C#编译为IL(中间语言,byte code)
          • 然后通过Mono运行时即VM中的编译器将IL编译成对应平台的原生码
          • 原生码是什么???











          • 三种转译方式
          • 即时编译(just in time,JIT):在程序运行过程中,将byte code转译为目标平台的原生码
          • 将IL代码转为对应平台的原生码并映射到虚拟内存中执行。编译时IL是依托Mono运行时,转为对应原生码后再依托本地运行
          • 为什么是虚拟内存











          • 提前编译(ahead of time,AOT):在程序运行之前,将.exe或.dll文件中的CIL的byte code部分转译为目标平台的原生码并且存储,程序运行中仍有部分CIL中的byte code需要JIT编译
          • 完全静态编译(full ahead of time,full-AOT):在程序运行之前,将所有源码编译成目标平台的原生码
          • 在程序运行之前编译是否也依托于Mono运行时???










          • 特点
          • 构建应用非常快
          • Mono的JIT机制可以支持更多的托管类库
          • 支持运行时代码执行
          • 必须将代码发布成托管程序集(.dll文件,由mono或者.net生成)
          • Mono VM在各个平台移植异常麻烦,有几个平台就得移植几个VM(WebGL和UWP只支持IL2CPP)
          • Mono版本受限,C#很多新特性无法使用
          • IOS仍然支持Mono,但不允许32位










        • IL2CPP

          • 组成
          • AOT(静态编译):将IL中间语言转换成CPP文件
          • 运行时库:例如垃圾回收、线程/文件获取(独立于平台,与平台无关)、内部调用直接修改托管数据结构的原生代码的服务与抽象










          • 特点
          • 转成CPP后运行效率更快,且可以调试生产的C++代码
          • Mono VM在各个平台的移植和维护非常耗时
          • 利用现成的在各个平台的C++编译器对代码执行编译器优化,这样可以进一步减少最终游戏的尺寸并提高游戏运行速度
          • 动态语言特性,所有内存分配和回收由GC组件完成
          • 尽管通过IL2CPP后代码转换成了静态的C++,但内存管理还是遵循C#的方式,因此还需要一个IL2CPP VM,它来提供诸如GC管理、线程创建这类服务性工作
          • IL2CPP可以做得很小,因为它不需要处理IL加载和动态解析,因此使得游戏载入时间缩短
          • 多平台移植非常方便
          • 相比Mono构建应用慢
          • 只支持AOT编译








    • 内存管理分类

      • 按照分配方式

        • Native Memory(原生内存):不会被系统自动管理,需要手动释放

          • Unity重载了C++的分配内存的操作符
          • 使用操作符时提供参数Memory Label指示将当前的内存分配到哪一个内存池
          • Unity在底层使用Allocator,每个Allocator池,单独做跟踪
          • NewAsRoot:使用Allocator的生成会使用NewAsRoot,生产Memory Island,它包括很多子内存
          • 返还系统:当使用delete和free内存时,会立刻返还给系统;这与托管内存不同,它需要GC才返回










        • Managed Memory(托管内存):系统自动管理,通过GC释放

          • VM内存池:即Mono虚拟机的内存池,以Block的形式管理,当一个Block连续6次GC没有被访问到,这块内存会被返回给系统
          • GC分类
          • 分代与非分代式
          • 非分代式:全都堆在一起,速度快
          • 分代式:划分不同区域,包括大内存、小内存和超小内存










          • 压缩和非压缩
          • 非压缩式:有内存被释放,这块区域空着
          • 压缩式:对空白区域重新排布,填充空白,使内存紧密排布










          • Incremental GC:解决原来GC导致的卡顿问题,将GC操作分帧进行










          • Unity采用Boehm GC,简单粗暴,属于非分代、非压缩式,会导致内存碎片化









        • 这个如何控制?

          • 堆栈(Stack)
          • 存储函数和值类型的地方
          • 堆栈回溯
          • 不会出现碎片化或GC问题,但可能会碰见堆栈溢出










          • 堆积(Heap)
          • 引用类型
          • 销毁对象后,内存空间不会马上释放,而是标记为未使用,之后由GC释放
          • 对象实例化和摧毁过程很慢;可能会导致碎片化
          • 错误引用,导致希望释放的内存未被正常释放,造成内存泄漏
          • 当内存不足以分配给一个存储占用较大的对象时
          • GC运行,尝试去释放更多的空间以满足对象的内存分配需求
          • 堆空间拓展










        • 官方文档的说明

          • 分为三层进行管理
          • 托管内存(Managed memory)
          • 重点在于托管堆(managed heap)和GC
          • 由Mono和IL2CPP‘s脚本虚拟机实现,也叫做脚本内存系统
          • 托管堆(managed heap):由虚拟机通过GC自动管理
          • 脚本栈(scripting stack):程序运行过程中的值类型和函数调用使用的内存
          • 原生虚拟器内存(Native VM memory):Unity脚本层相关的内存











          • 优缺点
          • 优点
          • 有助于防止内存泄漏
          • 保护内存访问










          • 缺点
          • 会影响运行时性能,因为分配托管内存对CPU来说非常耗时
          • GC也可能会阻止CPU做其他工作,直到它完成











          • C#非托管内存(C# unmanaged memory)
          • Unity Collections namespace and package,no use of GC
          • 允许访问原生内存以微调内存分配











          • 原生内存(Native memory)
          • 用来运行引擎的C++内存
          • Unity引擎的内部C/ c++核心拥有自己的内存管理系统,即原生内存
          • 通过C#的API可以间接访问原生内存











          • 非托管内存和原生内存有什么区别?是指的同一种内存吗?









      • Editor和Runtime模式:管理方式、内存大小、分配时间和分配方式都不同
      • 按照管理方式

        • 引擎管理内存

          • 即引擎运行时需要分配的内存,开发者一般触碰不到









        • 用户管理内存

          • 开发时使用的内存








    • Unity无法管理的内存

      • 用户分配的Native内存:比如自己写的C++ Native插件,因为Unity无法分析已编译的C++是如何分配和使用内存的
      • Lua:由自己管理,Unity没法统计其内部情况


懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-11-24 06:19 , Processed in 0.090460 second(s), 25 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表