欢迎光临散文网 会员登陆 & 注册

7_内存管理

2023-07-17 13:40 作者:Object404  | 我要投稿

Mono

  • Mono框架是Unity底层的一个组成部分,与Unity配合使用

  • 当编写C#代码时,Mono充当了C#脚本的承载环境

  • 打包时Mono会将C#代码编译成中间语言

  • 在运行时Mono会将中间语言编译为机器码,用来与Unity各个功能进行交互


IL2CPP

  • IL2CPP是Unity后来引入的脚本运行环境,用来取代Mono

  • IL2CPP的主要优势是在性能方面

  • IL2CPP在打包时就会将C#代码编译成中间语言,之后转化为C++代码,并转化为C++工程,在这个过程中IL2CPP还会对中间代码进行一些优化,最后再将C++代码编译成原生机器码,IL2CPP生成的机器码是与设备平台息息相关的

  • 在运行时就不需要其它工序,原生机器码会直接和Unity各个功能进行交互,同时这样做还可以还可以提高代码的安全性,防止反编译


中间语言

  • 这是一种与具体平台无关的中间代码,它不依赖于具体的硬件或操作系统

  • 中间语言的存在是让Unity可以在不同平台上运行的关键


托管语言

C#和C++的一个重要区别是C#是一种托管语言,而C++是一种非托管语言

托管语言意味着它的执行是在托管环境中运行的,托管语言的特点就是可以自动进行内存管理,包括对象的分配和释放,垃圾回收等,开发者不需要手动去管理内存资源,这会牺牲一些运行速度,但也可以用更简单的方式实现游戏逻辑

非托管语言需要开发者手动分配和释放内存,并负责管理对象的生命周期,C++可以直接访问底层硬件和操作系统资源,运行速度更快,但也需要花更多的时间在资源管理上


内存域

Unity中内存框架可以划分为3个不同的内存域,每个域存储不同的数据类型,关注不同的任务集

  • 托管堆

    • 这个地方是Mono平台工作的地方

    • 我们编写的任何脚本都会在这里变为实例化对象,脚本之间的交互也是在这里进行

    • 被称为托管堆是因为这里提供了自动内存管理和垃圾回收的功能

  • 本地域

    • Unity的底层代码会在这里运行,本地域不会进行自动的内存管理和垃圾回收,需要开发者自行处理

  • 外部库

    • Unity的插件会在这里运行,插件的内存管理和垃圾回收由插件的开发者负责,同时插件的开发者也可能会留些接口让其他人也可以控制插件的内存管理

  • 托管桥:负责连接托管代码和底层非托管代码的接口,当频繁通过托管桥调用非托管对象时会引起性能问题


运行时的内存空间

  • 用来存储值类型的数据和数据对象的引用

  • 栈的内存分配是有编译器自动处理的,不需要开发者手动管理

  • 用来存储引用类型的对象

  • 当一个对象在堆上没有任何引用指向它时,这个对象就会成为垃圾对象,垃圾收集器会在合适的时间回收它


内存碎片

内存是一个连续的储存空间,这段空间又被划分为不同的内存块来储存数据和对象

当内存中间被销毁时就会产生两个不连续的储存空间,这时如果需要分配一个大块的连续内存,但可用的内存又分布在不连续的区域上,就可能无法满足连续的内存分配需求,导致内存的浪费


内存分配流程

  1. 验证是否有足够的连续空间用于分配新对象

  2. 如果没有连续空间,则检查所有实例化的对象,并且筛选出不再被引用的对象

  3. 将不再被引用的对象进回收

  4. 再次验证是否有足够大的连续的内存空间

  5. 如果没有,从操作系统请求新的内存块

  6. 在新分配的块前分配新对象,并返回给调用者


内存优化

  • 纹理内存

    • 纹理的压缩格式

      • 导入到Unity的图片都会被转换成可以被GPU直接读取的图片格式(ETC,ASTC等)

      • 选择合适的压缩可是可以达到

      • 节省内存

      • 减少底宽

      • 降低加载耗时

    • MipMap

      • 可以优化远处物体的表现效果

      • 减少带宽

      • 场景纹理需要开启mip map,UI关闭mip map

    • Texture Quality(Edit - Project Settings - Quality -Texture Quality)

      • 可以限制加载进内存的MipMap层数,因此对开启了MipMap的纹理生效

      • Full Res:加载所有层级的MipMap

      • Half Res:不会加载MipMap0层级,会使用MipMap1代替MipMap0

      • Quarter Res:不会加载MipMap0和MipMap1这两层,当需要使用MipMap0时会用MipMap2代替

      • 可以在不同配置的机器上使用不同的设置,来进行画质分级

    • Read/write

      • 开启了Read/write选项Unity不仅会把纹理数据上传到GPU内存,还会上传到CPU内存

      • 没有对纹理的读写需求就不要开Read/write


  • 网格资源

    • Read/write

      • 开启了Read/write选项Unity不仅会把网格数据上传到GPU内存,还会上传到CPU内存

      • 没有对网格的读写需求就不要开Read/write

    • 顶点属性

  • 渲染的时候Shader并不一定会用到所有种类的顶点属性,这时多余的属性就会造成不必要的内存浪费

  • 设置Normals属性

    • 设置Tangent属性

  • 骨骼

  • 带有动画效果的模型必须有骨骼

  • 静态物体可以去掉骨骼

    • 这样设置就可以不导入模型的骨骼

    • 可以在导入模型前在建模软件中去掉骨骼


  • RenderTexture资源

  • 抗锯齿:为了减小锯齿,抗锯齿需要对每个像素进行多重采样,同时为了存储多重采样的结果需要建立中间缓冲区,这样就需要额外的内存来存储这些信息

    • 阴影分辨率:阴影分辨率决定了阴影纹理像素的数量,而像素数量直接影响内存占用


  • 音频资源

    • Force To Mono:开启这个选项音频就会变为双声道,这样内存占用就会增加一倍

    • 不同的音频加载方式会影响内存占用,可以参考之前的文章对音频设置合适的加载方式

    • 不同的音频压缩格式会影响内存占用,可以参考之前的文章对音频设置合适的压缩格式


  • 粒子系统

    • 粒子内内存的消耗与粒子在场景中的播放数量有关,低端机上可以关闭一些不必要的粒子特效以减少内存消耗


  • 粒子系统在初始化时会创建一个长度为最大粒子数的数组,如果实际播放的粒子数不是很多,但是最大例子数设置的很高的话,就会导致创建的数组太长,从而造成内存的浪费





7_内存管理的评论 (共 条)

分享到微博请遵守国家法律