unity游戏内存

转自:Unity的Mono内存管理_鳄鱼泪的博客-CSDN博客

转自:Unity游戏内存分布概览 - 知乎 (zhihu.com)

unity进程内存分布:

Unity中的内存种类:程序代码、本机堆(native heap)和托管堆(mono堆)。

1.程序代码

程序代码包括了所有unity引擎使用的库,以及你所写的所有游戏代码。在编译后,得到的运行文件将会被加载到设备中运行,并占用一定的内存。这部分内存实际上是没有办法去管理的,它们将在内存中从一开始到最后一直存在,想要减少这部分内存的使用,能做的就是减少使用的库。

2.本机堆(native heap)

本机堆是unity引擎进行申请和操作的地方,比如贴图、音效、关卡数据等。unity使用了自己的一套内存管理机制来使这块内存具有和托管堆类似的功能。基本理念是,如果在这个关卡里需要某个资源,那么在需要时就加载,周没有任何引用时进行卸载。听起来很美好也和托管堆一样,但是由于unity有一套自动加载和写在资源的机制,让两者变得差别巨大。自动加载资源可以为开发者省不少事儿,但是同时也意味着开发者失去了手动管理所有加载资源的权力,这非常容易导致大量的内存占用(贴图什么的你懂的),也是Unity给人留下“吃内存”印象的罪魁祸首。

优化:

(1).scene加载时,scene中的所有对象都将加载到内存中,包括scene中挂载了的脚本中赋值了的的材质,贴图,动画,声音等素材。这正是Unity的智能之处,不会再有额外的加载,这样的代价是内存占用将变多。在面向移动设备游戏的制作时,尽量减少在Hierarchy对资源的直接引用,而是使用Resource.Load的方法,在需要的时候从硬盘中读取资源,在使用后用Resource.UnloadAsset()和Resources.UnloadUnusedAssets()尽快将其卸载掉。

(2).在scene切换时,所有资源将会被卸载掉,除非是被标记了DontDestroyOnLoad的资源,如果DontDestroyOnLoad了一个包含很多资源,比如大量贴图或者声音等大内存占用的东西,这部分资源在场景切换时无法卸载,将一直占用内存,这种情况应该尽量避免。

(3).static和单例在场景切换时也不会被摧毁,如果这种单例含有大两的对资源的引用,也会占用内存,所以不用的时候将其置为null以便回收。

(4).Resource.UnloadAsset()和resources.UnloadUnusedAssets()时,只有那些真正没有任何引用的资源会被回收,因此确保资源不再使用时,将所有对该资源的引用设置为null或者destroy。同样需要注意,这两个Unload方法仅仅对Resource.Load拿到的资源有效,而不能回收任何场景开始时自动加载的资源。与此类似的还有AssetBundle的Load和Unload方法,灵活使用这些手动自愿加载和卸载的方法,是优化Unity内存占用的不二法则。

mono申请内存流程:当mono需要分配内存时,首先是会查看空闲内存是否足够,若足够的话,则是直接在空闲内存中分配,否则mono会进行一次GC以释放更多的空闲内存,如果GC之后仍然没有足够的空闲内存,则mono会向操作系统申请内存,并扩充堆内存,

3.托管堆

托管堆是被mono使用的一部分内存。mono是一个开源的.net框架的一种实现,对于unity开发,其实充当了基本类库的角色。托管堆用来存放类的实例,比如用new生成的List,实例中声明的变量等(个人观点:从这句话来看,托管堆应该就是管理没有继承自MonoBehaviour的类所产生的内存,因为继承自MonoBehaviour的类必须挂载到GameObject上才会生效,且不使用new,而GameObject是本机堆管理的资源,Instantiate创建的内存被创建在了unity里,不是mono里。)。

优化:

(1)在继承自MonoBehaviour的类中声明并分配的对象在不需要时要及时置为null,尽量不要等到destroy时再清理

(2)对象池

 mono通过GC进行内存管理,Mono内存分为两部分,已用内存(used)和堆内存(heap),已用内存它指的是mono实际需要使用的内存,堆内存指的是mono向操作系统申请的内存,两者的差值就是mono的空闲内存。

GC它的的主要作用是在于从已用内存中找出那些不再需要使用的内存,并且是进行释放。

除了空闲内存不足时mono会自动调用GC外,也可以在代码中调用GC.Collect()手动进行GC,但是,GC本身是比较耗时的操作,而且由于GC会暂停那些需要mono内存分配的线程(C#代码创建的线程和主线程),因此无论是否在主线程中调用,GC都会导致游戏一定程度的卡顿,需要谨慎处理。另外,GC释放的内存只会留给mono使用,并不会交还给操作系统,因此mono堆内存是只增不减的。

 mono内存泄漏:对象已经没有用(需要被GC)却没被GC回收。大部分内存泄漏的情况都是因为静态对象的引用,因此尽量少用静态对象或者保证不再需要静态对象后将其引用设置为null,使其可以回收。

profilter视图

Simple:

 usedTotal:使用中的内存,reserved:GC后未使用的内存(不会释放,申请前先从这里面拿)

Unity:所有Unity申请和管理的内存减掉<profilter>,<FMOD>和<Video>。包含Mono堆。

GfxDriver:GPU显存开销,主要由Texture,VertexBuffer以及indexBuffer组成。但不包括Render Targets。(也不包含其他平台的驱动层)

FMOD:音频插件

Viedo:视频

Profilter:分析器自身开销

Detailed:

  • Assets — 当前从 scenes, Resources 和 Asset Bundles加载的总资源。
  • Built-in Resources — Unity Editor 资源 或者 Unity default 资源,
  • Not Saved — 被标记为 DontSave的GameObjects
  • Scene Memory — GameObject和它附属的Components
  • Other — 其他不在上面几条分类中的。

 除了unity自带的工具,还有许多第三方工具:uwa,upr等,但他们还是依赖unity自身提供的profilterapi,换句话说,测量的结果没什么不同,只是显示的差异。

原文地址:https://www.cnblogs.com/mcyushao/p/15015587.html