性能优化(34)——内存优化(3)——内存指标术语与进程内存介绍


 

  • 几乎在所有操作系统上,虚拟内存与物理内存都是以Page来组织的
  • Page是操作系统管理内存的最小基础单元,通常为4k大小,但在不同的操作系统和平台上可能有不同,比如在比较新的IOS或Mac操作系统上是16k大小
  • 一般,电脑中的CPU会有一个内存管理单元,MMU,它会维护一张Page的表,并将虚拟地址映射到物理内存地址上。当用户访问虚拟地址时,会自动被MMU转换为物理地址。但当CPU访问虚拟地址并没有找到映射的物理内存地址时,CPU会触发Page fault中断当前程序执行,然后再分配一块干净的物理内存,并从磁盘中加载所需的一页数据到该物理地址,同时更新列表并继续执行程序
  • 当一个进程向系统申请内存时,系统并不会返回物理内存地址,而是返回一个虚拟内存地址,仅当CPU需要访问该虚拟内存地址时,系统才会分配并映射到物理内存上
  • 每个Page可能有不同的状态,用来描述该内存页处理的不同事务状态
    • Used代表Page正在被进程使用,而内存页也已经被映射到了物理内存中
    • Free代表该Page页可用,并且该内存页还没有被映射到物理内存中
    • Cache状态代表该页被操作系统用于缓存的可用内存,虽然内存页已经被映射到物理内存中,但该页可能近期没有被访问
  • 如果Free Page数量低于一个阈值时,操作系统会从Cache的页中去获取内存,并将Cache页上的数据交换到磁盘,然后对Cache的页进行清理,并将其标记为Free Page

  • 在内存管理的更高级别上,操作系统的虚拟内存还会将Page组织成区域。可能在不同的操作系统中使用不同的名称来定义这个概念,这里统称为Region,它是一块共享内存状态和保护级别的连续地址空间。它可以由具有不同的页面状态的多个Page组成
  • 当然Region也有不同的状态:
    • Resident:代表其中的配置页已经在物理内存中
    • Dirty:代表配置已修改,但未写入磁盘
    • Wired:代表永远不会交换到辅助存储的固定配置段
    • Committed:代表已分配的内存区域,其地址不能被其他分配使用,是由RAM磁盘上的分页文件或其他资源来支持的,访问权限由内存的保护级别控制
    • Reserved:该状态的Region保留地址空间,供将来使用,地址不会被其他分配使用,也不可访问,如果尝试从Reserved的内存读写,都会导致访问冲突异常,并且它没有与之关联的物理存储支持
    • Free:是空闲内存区,既不会Committed,也不会Reserved,并且进程无法访问,同样任何尝试读写Free状态的Region也会导致访问冲突异常
  • Region的类型:
    • Anonymous:是匿名的,这类Region上的内存页,与文件系统上的文件并没有关联。例如,C++使用malloc的任何分配,或mono上的分配都是匿名的
    • Mapped:映射类型,这类Region包含的内存页,是以文件系统设备节点关联的,文件与内存将做直接映射

一个进程的内存结构

  • Committed Memory
    • 是一个进程分配虚拟内存的总量
    • 从纵向看,包括物理内存、Mapped Files、Swapped Files、Compressed Memory四部分
    • 从横向看,包括当前进程私有占用的内存,和与其他进程共享的内存
  • Resident Memory
    • 已经被映射到虚拟内存中的物理内存
    • 同样包括当前进程占用的内存,和与其他进程共享的内存
    • 其中存在一些非代码执行的开销,如系统或应用的二进制加载所占用的内存

进程的内存结构(进一步细分)

  • 可分为:
    • VSS:即总共Commit提交的Regions内存总和
    • RSS:进程可访问的Resident常驻内存总和
    • PSS:当前内存常驻内存与其他进程共享的Resident内存总和
    • USS:为当前进程私有的Resident内存总和

  • Unity下提供的Memory Profiler工具,总体内存是对Commited Memory即VSS进行衡量的,而并不涉及是否为Resident Memory
  • Windows平台下,Windows任务管理器中的Memory指标是针对于USS来衡量的
  • Mac与IOS下,XCode显示的内存衡量的是RSS加上压缩后的内存交换页的大小

  • 如果大于此经验值,也不一定完全不行。游戏类型不同,各类资源占用也会有所差异
  • 另外,System.XXX总和这一项的内存开销基本来自于用户的配置数据表,与C#层的基础类型变量的定义。一些开发者习惯把所有配置数据全部缓存进来,会导致此项内存开销过高,尤其是一些纯文本数据,如果真的需要,也建议采用二进制方式分级缓存读取
  • 还有其他各类对象单选这条,是指一些C#层的对象,主要会是一些Component与GameObject派生出来的对象,我们不仅需要关注这些对象所占用的内存大小,还需要关注对象个数