实际上Unity游戏使用的内存一共有三种:程序代码、托管堆(Managed Heap)以及本机堆(Native Heap)。
程序代码包括了所有的Unity引擎,使用的库,以及你所写的所有的游戏代码。在编译后,得到的运行文件将会被加载到设备中执行,并占用一定内存。 这部分内存实际上是没有办法去“管理”的,它们将在内存中从一开始到最后一直存在。一个空的Unity默认场景,什么代码都不放,在iOS设备上占用内存应该在17MB左右,而加上一些自己的代码很容易就飙到20MB左右。想要减少这部分内存的使用,能做的就是减少使用的库,稍后再说。
托管堆是被Mono使用的一部分内存。Mono项目一个开源的.net框架的一种实现,对于Unity开发,其实充当了基本类库的角色。 托管堆用来存放类的实例(比如用new生成的列表,实例中的各种声明的变量等)。“托管”的意思是Mono“应该”自动地改变堆的大小来适应你所需要的内存, 并且定时地使用垃圾回收(Garbage Collect)来释放已经不需要的内存。关键在于,有时候你会忘记清除对已经不需要再使用的内存的引用, 从而导致Mono认为这块内存一直有用,而无法回收。
最后,本机堆是Unity引擎进行申请和操作的地方,比如贴图,音效,关卡数据等。Unity使用了自己的一套内存管理机制来使这块内存具有和托管堆类似的功能。基本理念是,如果在这个关卡里需要某个资源,那么在需要时就加载,之后在没有任何引用时进行卸载。听起来很美好也和托管堆一样,但是由于Unity有一套自动加载和卸载资源的机制,让两者变得差别很大。自动加载资源可以为开发者省不少事儿,但是同时也意味着开发者失去了手动管理所有加载资源的权力,这非常容易导致大量的内存占用(贴图什么的你懂的),也是Unity给人留下“吃内存”印象的罪魁祸首。
Unity has already created a basic guide on using Instruments to profile iOS games. It can be found here.
https://blogs.unity3d.com/cn/2016/02/01/profiling-with-instruments/
进入Detialed,单击Take Sample: Editor可以看到以下几类内存数据
Assets: Asset referenced from user or native code (含有code里创建的game object)
Built-in Resources: Unity Editor resources or Unity default resources
Not Saved: GameObjects marked as DontSave
Scene Memory: GameObject and attached components (在当前scene里创建的game object)
When loading a new level all objects in the scene are destroyed, then the objects in the new level are loaded.
In order to preserve an object during level loading call DontDestroyOnLoad
on it. If the object is a component or game object then its entire transform hierarchy will not be destroyed either.
Other: GameObjects not marked in the above categories 项目中通过代码生成的各种资源记录
Ref: http://blog.csdn.net/quan2008happy/article/details/39352535
DontSave:保留对象到新场景
功能说明:此属性的功能是用来设置是否将Object对象保留到新的场景(Scene)中,如果使用HideFlags.DontSave,则Object对象将在新场景中被保留下来,对其使用说明如下。
(1)如果GameObject对象被HideFlags.DontSave标识,则在新scene中GameObject的所有组件将被保留下来,但其子类GameObject对象不会被保留到新scene中。
(2)不可以对GameObject对象的某个组件如Transform进行HideFlags.DontSave标识,否则无效。
(3)即使程序已经退出,被HideFlags.DontSave标识的对象会一直存在于程序中,造成内存泄漏,对HideFlags.DontSave标识的对象在不需要或程序退出时需要使用DestroyImmediate手动销毁。
官方文档 https://docs.unity3d.com/ScriptReference/HideFlags.DontSave.html
The object will not be saved to the scene. It will not be destroyed when a new scene is loaded. It is a shortcut for HideFlags.DontSaveInBuild | HideFlags.DontSaveInEditor | HideFlags.DontUnloadUnusedAsset.
It is your responsibility to cleanup the object manually using DestroyImmediate, otherwise it will leak.
上面所提到的托管堆
相关
我们值得关注的东西
library/unity default resources
和resouces.asssets
的两个文件。AssetBundle相关可以通过调用Unload(false)释放AssetBundle,从而释放对应的SerializedFile。Metadata相关在切换UI后会自动出现/消失,怀疑是在预加载UI这一类的。library/unity default resources
也会不时消失,resouces.asssets
似乎一直都在。不过值得欣慰的是这些不时出现的SerializedFile每个都在20K以内,而比较大的AssetBundle时产生的SerializedFile我们可以解决。Asset loading can also be identified in CPU traces. The main method indicating an Asset load is
SerializedFile::ReadObject
. This method connects a binary data stream (from a file) to Unity’s serialization system, which operates via a method namedTransfer
. TheTransfer
method can be found on all Asset types, such as Textures, MonoBehaviours and Particle Systems.This (scene loading) requires Unity to read and deserialize all the Assets within the Scene, as denoted by the calls to various
Transfer
methods beneathSerializedFile::ReadObject
.
一些我们并没什么办法的东西
System.ExecutableAndDlls: 系统可执行程序和DLL,是只读的内存,用来执行所有的脚本和DLL引用。(存疑:不同平台和不同硬件得到的值会不一样,可以通过修改Player Setting的Stripping Level来调节大小。)
GfxClientDevice: GFX(图形加速\图形加速器\显卡 (GraphicsForce Express))客户端设备。
ShaderLab: Unity自带的着色器语言工具相关资源。
PersistentManager.Remapper: 持久化数据重映射管理相关
与持久化数据相关,比如AssetBundle之类的。注意监控相关的文件。(存疑!实验load assetbundle并不会导致这部分内存增加。不过这部分一般只占18KB左右。
GameObject: code里创建的game object
sprite
如果load一些asset再回到原来的UI,这部分基本没有变化。因为它存储的是当前的场景中各个方面的内存占用情况。
包括GameObject、所用资源、各种组件以及GameManager等(一般情况通过AssetBundle加载的不会显示在这里)
GameObject and attached components
Script Serialization: https://docs.unity3d.com/Manual/script-Serialization.html ↩
SerializedFile: https://docs.unity3d.com/Manual/BestPracticeUnderstandingPerformanceInUnity1.html ↩