【Unity3D】Unity内存解析 (二)

786 阅读4分钟

本文已参与[新人创作礼]活动,一起开启掘金创作之路。

这一部分是视频记录的第二部分,第一部分见: juejin.cn/post/708775…

4、关于Unity内存

Unity内存的分类

Unity内存分为 Native Memory和 Managed Memory (托管内存)。值得注意的是,在Editor下和在Runtime下Unity的内存分配是完全不同的。不但分配内存的大小会有不同,甚至分配的时机、方式都会有所不同。

比如一个AssetBundle,在编辑器下是你一打开Unity就开始加载,而在Runtime下则是你使用时才会加载。(Unity2019之后做了一些Asset导入优化,不使用的资源就不会导入)。

Unity的内存还可以分为引擎管理的内存和用户管理器的内存两类。引擎管理的内存一般开发者是访问不到的,而用户管理的内存才是使用者需要关系和优先考虑的。

Unity监测不到的内存

用户分配的 Native 内存内存是Unity的Profile工具监测不到。例如用户写的C++Native插件、以及Lua分配的内存。

5、关于Unity Native内存的控制:

Scene

当一个Scene中有过多的GameObject存在的时候,Unity Native 内存就会显著上升。

Audio(音频):

DSP Buffer :相当于音频的缓冲。当DSP Buffer过大时,会导致声音延迟(需要更多的音频数据来填充DSP Buffer)。如果DSP Buffer过小,会导致CPU负担上升。

Foce to Mono : 强制单声道(当两个声道完全相同时可以Force To Mono),可以节省一半的内存。

Format:例如IOS对MP3有硬解支持的,所以MP3的解析会快很多(Android 没有)。

Compressiont Format:声音文件在内存的存在形态(解压的、压缩的等)。

Code Size:

代码也是需要加载进内存的,使用时要注意减少模板泛型的滥用。因为模板泛型在编译成C++时,会把同样的代码排列组合都编译一边,导致Code Size 大幅上升。

AssetBundle:

TypeTree : 当前版本所用到的各种版本数据序列化类型。这个东西其实是Unity在做版本兼容的时候有用,保证Unity能向下兼容。当你确定你当前的AssetBundle和你的Unity是同一个版本的时候,就可以关掉TypeTree。关掉TypeTree之后可以减少内存大小、包大小、加快运行速度。

压缩方式:使用Lz4,而不是Lzma;

Size & Count:AssetBundle包的大小、数量。一般合适的Asb包大小大概是1M~2M的大小,可适量加大。

Resources文件夹:

Resources会在打包时生成一个红黑树,当资源过多时会相应地导致红黑树也变大,还会极大增加游戏的启动时间。另外Resources文件夹下的资源是不会卸载的,会存在于整个应用的生命周期。

尽量使用AssetBundle而不是Resources。

Texture:

Upload Buffer,和声音的DSP Buffer,设置填充满多大之后再推向CPU/GPU。

Read/Write : 不使用就关闭它。正常情况下,当Texture被推给 CPU/GPU之后,内存就会把他删除,除非你打开了 Read&Write。

Mip Map : 像UI这些不需要的就关闭它。

Mesh:

Read/Write :同上

Compression:虽然写的是压缩,但实际效果并不一定有用,甚至还有副作用,建议不开。

6、Unity Managed Memory (托管内存):

VM内存池:

Mono虚拟机的内存池,实际上VM是会返回给操作系统。返回条件是当一块内存(Block)连续6次没有被访问到时,就会被返回给操作系统。当然这个返回基本看不到,尤其是在Mono Runtime的时候。

GC机制:

Unity的GC机制是Boehm内存回收,是不分代的,非压缩式的。(之所以是使用Boehm是因为Unity和Mono的一些历史原因,以及目前Unity主要精力放在IL2CPP上面)

Incremental GC(渐进式GC),现已实装。Incremental GC可以改善GC时的主线程卡顿,他把GC的工作分多帧进行。

IL2CPP的GC机制是Unity重写的Boehm。

为了防止内存碎片化(Memory Fragmentation),在做加载的时候,应先加载大内存的资源,再加载小内存的资源(因为Bohem没有内存压缩),这样可以保证最大限度地利用内存。

Zombie Memory(僵尸内存):无用内存或者没有释放的内存。通过代码管理和性能工具分析,查看各个资源的引用,避免僵尸内存的出现。

管理技巧:

1、用Destory而不是NULL 。

2、多使用Struct。

3、使用内存池(UI、粒子系统等)

4、闭包和匿名函数:减少使用。所有的闭包和匿名函数最后都会变成一个Class。

5、协程:只要不被释放,里面所有引用的所有内存都会存在。(用的时候生产一个,不用的时候扔掉)。

6、配置表:减少一次性使用的配置表数量;

7、单例:慎用,会长期占用内存。

后记:

建议各位Unity开发者去听听原视频,会收获更多更详细~