返回「计算机、信息技术与工程」

Unity Profiler

更多
Markdown 结构化数据
本文目录 51 个章节

Unity Profiler

warning · 历史版本边界 本文主体来自 2020 年 Unity Profiler 使用记录,模块名、统计口径和平台行为可能已变化。保留的阈值与经验只能作为历史项目线索;当前项目应在目标 Unity 版本、设备和构建配置上重新建立基线,并结合 Memory Profiler、Android/iOS 系统工具交叉验证。

简介

Unity Profiler 展示 Unity 能追踪的引擎、托管堆和部分图形等内存,并不等于操作系统统计的进程全部物理内存。第三方原生库、驱动、系统分配器与共享页面可能无法按同一口径归类。

注意

真实环境内存

设备上的系统指标(例如 Android PSS、iOS Instruments 所见进程内存)可能高于 Unity Profiler 的统计,也可能因采样时间和分类口径出现其他差异。

因为这些内存包括2部分

  • 真实的 used 的物理内存
  • 缓存的(cached)的物理内存,这是不同平台决定的.Android 和 iOS 系统在资源不使用的时候均不会立即将其进行回收,而是将其放在缓存的物理内存中,以便下次再使用时,可以快速的加载.

不能把 Unity Profiler 简化为“只记录真实使用的物理内存”。应分别记录 Unity 指标、系统 PSS/RSS/footprint 与采样条件,再解释差值。

系统在内存压力下会回收缓存、压缩或换出页面,并可能终止进程;具体机制随平台和系统版本变化。

PersistentManager.Remapper

主要提供文件的持久化存储,包括各种序列化的 asset,项目的 setting 文件,维护文件系统中的文件与内存中数据的对应关系.如果项目中大量适用 AssetBundle ,在对 AsserBundle 进行 Unload 之前都会需要占用 Remapper 的内存的,而 Remapper 本身的实现适用内存池,其数值只会增大,那么为了使 Remapper 占用的内存保持稳定,则在加载一定数量的 AssetBundle 之后需要进行 Unload 操作,而不要一次性把所有的 AssetBundle 进行 Unload.(这样的操作对维持整个 mono heap 的大小也是重要的,mono heap 本身也是只增大不减小的)

分析结果

Unity 在本机联合代码中分配的内存数量

Mono 托管代码所使用的堆大小和堆大小。这个内存是垃圾收集的

GfxDriver 是 Unity 对图形驱动相关分配的估计分类,可能涉及纹理、渲染目标、着色器、网格及驱动开销;它不是“显卡型号”或 GraphicsForce Express 的缩写。

FMOD 音频驱动程序的内存使用情况

Profiler 用于分析器数据的内存

内存统计信息显示在一些最常见的asset/object类型中。这些统计数据包括计数和使用的内存:

Textures

Meshes

Materials

Animations

Audio

Object Count

游戏对象在内存中常见的原因包括:

Assets:从用户或本机代码引用的资产

Built-in Resources:统一编辑器资源或统一缺省资源

Not Saved:标记为DontSave的GameObjects

Scene Memory: GameObject和附加组件

Other: 在上面的类别中没有标记的配子

Profiler UI显示数据

Uesd Total :

当前帧的Unity内存,Mono内存,GfxDriver内存,Profiler内存以及额外内存的总和。

Used total = Unity + Mono + GfxDriver + Profiler + additionalUsedMemory.

additionalUsedMemory在profiler 中没有显示,因为在大多数平台下都为 0(PC/Android/iOS 都为 0),一般情况下 Used Total 的值就是除 FMOD 之外各项的总和了.Reserved Total 同样适用的.

Reserved Total:

系统在当前帧申请的总体物理内存

Total System Memory Usage:

当前帧的虚拟内存使用量

GameObjects in Scene:

当前帧场景中的GameObject数量

Total Objects in Scene:

当前帧场景中的Object数量(除GameObject外,还有各种Component)

Total Object Count:

所有Object数量

API UnityEngine.Profiling.Profiler

使用要求

in your built app

most of the Profiler API functionality is only available when “Development Build” is enabled. This means you must enable “Developer Build” if you want to use profiler API methods in your built app.

Disabling “Development Build” makes your app run faster, but prevents you from using most of the Profiler API methods.

The exception to this are the Profiler API methods relating to memory usage. Because Unity manages most of its system memory at run-time, it can provide that information with no performance penalty, therefore those methods are available even if “Development Build” is not enabled.

Static Properties

usedHeapSizeLong

Returns the number of bytes that Unity has allocated. This does not include bytes allocated by external libraries or drivers.

Static Methods

GetAllocatedMemoryForGraphicsDriver

Returns the amount of allocated memory for the graphics driver, in bytes.

Only available in development players and editor.

GetMonoHeapSizeLong (Reserved Mono)

Returns the size of the reserved space for managed-memory.

GetMonoUsedSizeLong (Used Mono)

The allocated managed-memory for live objects and non-collected objects.

GetRuntimeMemorySizeLong (native-memory by Unity Obj)

Gathers the native-memory used by a Unity object.

GetTempAllocatorSize

Returns the size of the temp allocator.

GetTotalAllocatedMemoryLong

The total memory allocated by the internal allocators in Unity.

Unity reserves large pools of memory from the system. This function returns the amount of used memory in those pools.

GetTotalReservedMemoryLong

The total memory Unity has reserved.

GetTotalUnusedReservedMemoryLong

Unity allocates memory in pools for usage when unity needs to allocate memory. This function returns the amount of unused memory in these pools.

API和UI界面数据对应关系Table

Profiler UI界面 API
Reserved Unity GetTotalReservedMemoryLong
Used Unity GetTotalAllocatedMemoryLong
Reserved Mono GetMonoHeapSizeLong
Used Mono GetMonoUsedSizeLong

优化项目(对于选定帧)

note · 使用方法 以下条目保留旧版界面术语作为排查索引。“忽略”“无法优化”和固定数值均不是通用结论;先用快照差异、对象生命周期和系统内存趋势确认瓶颈,再决定是否优化。

显示的项

选中后,unity会自动获取这一帧的内存占用数据项,主要分为:Other、Assets、BuiltinResources、Scene Memory、NotSaved这五大部分

Other

System.ExecutableAndDlls

统可执行程序和DLL,是只读的内存,用来执行所有的脚本和DLL引用。不同平台和不同硬件得到的值会不一样,可以通过修改Player Setting的Stripping Level来调节大小。

Ricky:我试着修改了一下Stripping Level似乎没什么改变,感觉虽占用内存大但不会影响游戏运行。我们暂时忽略它吧(- -)!

GfxDeviceClient

图形设备客户端及其相关内存。具体统计范围取决于 Unity、图形 API、驱动和平台。

历史项目记录认为该项多为基础开销;当前项目仍应通过纹理、网格、渲染目标、着色器变体和图形 API 对照实验判断是否可降低。

ManagedHeap.UsedSize

托管堆使用大小。

历史项目曾把该项作为重点监控对象。不存在跨项目通用的 20 MB 上限;预算应结合设备内存、GC 策略、峰值分配和帧时间制定。

ShaderLab

Unity自带的着色器语言工具相关资源。

Ricky:这个东西大家都比较熟悉了,忽略它吧。

SerializedFile

序列化文件,把显示中的Prefab、Atlas和metadata等资源加载进内存。

Ricky:重点监控对象,这里就是你要监控的哪些预设在序列化中在内存中占用大小,根据需求进行优化。

PersistentManager.Remapper

持久化数据重映射管理相关

Ricky:与持久化数据相关,比如AssetBundle之类的。注意监控相关的文件。

ManagedHeap.ReservedUnusedSize

托管堆预留不使用内存大小,只由Mono使用。

Ricky:无法优化。

Assets

Texture2D

2D贴图及纹理。

Ricky:重点优化对象,有以下几点可以优化:

  1. 许多贴图采用的Format格式是ARGB 32 bit所以保真度很高但占用的内存也很大。在不失真的前提下,适当压缩贴图,使用ARGB 16 bit就会减少一倍,如果继续Android采用RGBA Compressed ETC2 8 bits(iOS采用RGBA Compressed PVRTC 4 bits),又可以再减少一倍。把不需要透贴但有alpha通道的贴图,全都转换格式Android:RGB Compressed ETC 4 bits,iOS:RGB Compressed PVRTC 4 bits。
  2. 当加载一个新的Prefab或贴图,不及时回收,它就会永驻在内存中,就算切换场景也不会销毁。应该确定物体不再使用或长时间不使用就先把物体制空(null),然后调用Resources.UnloadUnusedAssets(),才能真正释放内存。
  3. 有大量空白的图集贴图,可以用TexturePacker等工具进行优化或考虑合并到其他图集中。
AudioManager

音频管理器

Ricky:随着音频文件的增多而增大。

AudioClip

音效及声音文件

Ricky:重点优化对象,播放时长较长的音乐文件需要进行压缩成.mp3或.ogg格式,时长较短的音效文件可以使用.wav 或.aiff格式。

Cubemap

立方图纹理

Ricky:这个一般在天空盒中比较常见,我也不知道如何优化这个。。。

Mesh

模型网格

Ricky:主要检查是否有重复的资源,还有尽量减少点面数。

Scene Memory

Mesh

场景中使用的网格模型

Ricky:注意网格模型的点面数,能合并的mesh尽量合并。

Builtin Resources

Ricky:这些都是Unity的一些内部资源,对于项目内存没有什么分析价值,所以我就暂时不对其进行分析了。

Profiler内存重点关注优化项目

  1. ManagedHeap.UsedSize:监控稳定场景、峰值与多轮操作后的趋势;历史材料中的 20 MB 仅是旧项目经验,不作为当前通用阈值。
  2. SerializedFile: 通过异步加载(LoadFromCache、WWW等)的时候留下的序列化文件,可监视是否被卸载.
  3. WebStream: 通过异步WWW下载的资源文件在内存中的解压版本,比SerializedFile大几倍或几十倍,
  4. Texture2D: 重点检查是否有重复资源和超大Memory是否需要压缩等.
  5. AnimationClip: 重点检查是否有重复资源.
  6. Mesh: 重点检查是否有重复资源.

项目中可能遇到的问题

1.Device.Present:

1)GPU的presentdevice确实非常耗时,一般出现在使用了非常复杂的shader.

2)GPU运行的非常快,而由于Vsync的原因,使得它需要等待较长的时间.

3)同样是Vsync的原因,但其他线程非常耗时,所以导致该等待时间很长,比如:过量AssetBundle加载时容易出现该问题.

4)Shader.CreateGPUProgram:Shader在runtime阶段(非预加载)会出现卡顿(华为K3V2芯片).

5)StackTraceUtility.PostprocessStacktrace()和StackTraceUtility.ExtractStackTrace(): 一般是由Debug.Log或类似API造成,游戏发布后需将Debug API进行屏蔽。

2.Overhead:

1)一般情况为Vsync所致.

2)通常出现在Android设备上.

3.GC.Collect:

原因:

1)代码分配内存过量(恶性的)

2)一定时间间隔由系统调用(良性的).

占用时间:

1)与现有Garbage size相关

2)与剩余内存使用颗粒相关(比如场景物件过多,利用率低的情况下,GC释放后需要做内存重排)

4.GarbageCollectAssetsProfile:

1)引擎在执行UnloadUnusedAssets操作(该操作是比较耗时的,建议在切场景的时候进行)。

2)尽可能地避免使用Unity内建GUI,避免GUI.Repaint过渡GCAllow.

3)if(other.tag == a.tag)改为other.CompareTag(a.tag).因为other.tag为产生180B的GC Allow.

4)少用foreach,因为每次foreach为产生一个enumerator(约16B的内存分配),尽量改为for.

5)Lambda表达式,使用不当会产生内存泄漏.

5.尽量少用LINQ:

1)部分功能无法在某些平台使用.

2)会分配大量GC Allow.

6.控制StartCoroutine的次数:

1)开启一个Coroutine(协程),至少分配37B的内存.

2)Coroutine类的实例 -> 21B.

3)Enumerator -> 16B.

7.使用StringBuilder替代字符串直接连接.

8.缓存组件:

1)每次GetComponent均会分配一定的GC Allow.

2)每次Object.name都会分配39B的堆内存.

Ref

https://www.cnblogs.com/yanghui0702/p/yanghui20181026.html

https://zhuanlan.zhihu.com/p/139235851

https://www.jianshu.com/p/3c5540637525