使用内存剖析器分析应用程序的物理内存占用情况

要有效检测内存问题并优化性能,内存剖析器上显示的信息以及信息的精确度是关键。我们在这方面投入了大量精力。在最近的两篇博客中,我的团队成员介绍了Memory Profiler 1.0.0,并分享了诊断和检查游戏中内存相关问题的五个关键工作流程。
不久,我们将发布 Memory Profiler 1.1(实验版现已发布),其中包括更新的标签和说明,以解释内存如何工作以及如何计算应用程序内存占用。
由于内存占用一直是我们与开发人员交流的热门话题,因此我在这里回答大家经常提出的问题,特别是这三个主题:
- 什么是常驻内存
- 如何计算应用程序内存占用
- 如何分析内存占用
让我们深入了解一下 Unity 中的内存分配。当引擎分配内存时,它会首先在虚拟地址空间中预留多个内存页,以满足请求的分配。页是内存管理的最小单位。虚拟地址空间和物理存储空间各分为若干页,页的大小取决于所使用的平台。例如,在 x86 计算机上,页面大小为 4 KB。
在引擎预留了足够的页面后,它会要求操作系统(OS)将物理存储 "提交 "到内存中。这就是为什么分配的内存经常被称为 "已提交 "内存的原因。接下来,操作系统会注册这些页面现在已分配了物理存储空间,可以对它们进行访问。这样,应用程序报告的 "总承诺内存 "就会增加。不过,应用程序的物理内存占用空间保持不变。

占用空间保持不变的原因是,即使你已将区域存储到物理存储中,但大多数操作系统都是懒惰而节俭的,因此不会分配特定的物理存储位置。举个例子,假设您决定在 "已提交 "区域写一些内容。该区域下还没有任何物理内存,因此访问该区域会产生页错误。作为回应,操作系统的内存管理器将分配一个先前可用的物理页,以便完成您的操作。由于所有操作都是以页面大小为粒度执行的,因此区域中未被访问的页面将保持空闲,且未分配物理内存。同样,应用程序的常驻内存大小也会增加,增加的幅度是为完成操作而分配的所有物理内存页的总大小。

如果页面有一段时间未被访问,或者对物理内存的需求很高,操作系统可能会将分配区域中的一些页面卸载到压缩内存或页面交换文件中,具体取决于平台的支持情况。

在这种情况下,应用程序报告的已分配内存将保持不变,但常驻内存大小会减少。
正如你已经意识到的,如果你只查看已分配的内存,你可能会被哪些分配占用了你的物理内存所误导,这可能会诱使你优化一些没有问题的东西。这不仅浪费了您宝贵的时间,而且您也看不到应用程序的性能和稳定性有何不同。
总的来说,你的应用程序内存状态可以用这个图来描述:

总之,内存占用的计算方法如下:
物理内存占用 = 应用程序常驻内存 + 应用程序压缩内存页
在 Memory Profiler 1.1 中,"Summary(摘要)"、"Unity Objects(统一对象)"和" All Of Memory(全部内存)"视图不仅显示已 分配内存大小,还提供驻留内存信息。不过,只有在使用 Unity 2023.1 或更新版本制作内存剖析器快照时,才会显示此信息。使用旧版快照,你仍能看到更新的用户界面和故障视图,但没有驻留内存的信息。

摘要 "视图提供总体概览和基本指标:设备驻留总人数。如果您的应用程序需要在内存有限的平台上运行,"设备总驻留 "对于查看低内存警告和内存不足驱逐至关重要。根据经验,不应超过设备可用物理内存总量的 70%。
要进行详细分析,可以使用 Unity Objects 和 All of Memory 视图。你需要从下拉菜单中选择 "驻留在设备上"或 "已分配并驻留在设备上",并按 "驻留大小"排序,以查看对总物理内存使用量贡献最大的对象。

在分析常驻内存使用情况时,请记住
- 托管 内存将以常驻内存为主。Mono Heap 和 Boehm Garbage Collector 会定期访问对象并使其常驻。
- 图形内存(估计值) 显示为估计值。在大多数平台上,我们无法获得图形资源的确切位置信息,因此我们会根据宽度、高度、深度、像素格式等可用信息来估计大小。这也意味着我们没有关于图形资源居住状况的信息。出于可用性考虑,所有图形对象仅在 "分配 "视图模式下显示。
- 未跟踪内存是操作系统报告的由应用程序分配的所有内存,但缺乏有关分配来源的可靠信息。可能是本地插件、操作系统库、线程栈等。在某些平台上,我们会提供更多信息,让用户了解是谁在分组明细中分配了内存。
本地 内存包含对象使用的所有统一非托管分配,在分析本地 内存时,你会看到保留内存项。这是 Unity 内存管理器分配的内存,但在捕获过程中未被任何 Unity 对象使用。以下是一些有用的信息:
- 预留内存可能是常驻内存,这意味着最近可能删除了一个对象。
- 您可以进入 Memory Profiler 设置,启用 "显示预留内存明细 "复选框,获取有关预留明细的其他信息。默认情况下,该功能是禁用的,因为保留明细并不总是包含足够的可操作信息,而且需要深入了解 Unity 内存管理器的工作原理。
- 有关 Unity 内存管理器和分配策略的更多信息,请参阅分配器设置文档。
在某些平台上,如果特定平台组的规模较大,我们会显示额外的特定平台组,如Android上的Android Runtime。以下是有关 Android Runtime 的一些说明:
- 在某些版本中,Android Runtime 往往会预先分配大量内存,但从不使用。在这种情况下,分配的内存不会增加应用程序的内存占用,只需考虑常驻内存部分。
- 如果 Android Runtime 驻留部分占用了大量应用程序内存,请使用 Android Studio 剖析器分析在 Java 中进行的分配。
- 虽然安卓系统默认情况下没有页面文件或内存压缩,但 Linux 内核允许应用程序超量分配和分配比实际可用内存更多的内存。
- 捕捉时,请确保您了解所使用的设备。一些供应商为 Android Linux 内核提供内存压缩(zRAM)或供应商定制的页面交换文件工具。
我们希望这篇关于 Memory Profiler 1.1(实验版现已发布)的概述以及围绕内存占用的各种主题的探讨能对您有所帮助。
我和我的团队计划继续改进内存剖析器,以提供更精确、更有针对性的信息,并就潜在的内存不足情况及其可能的接近程度发出警告。关注我们产品路线图的进展,并告诉我们您的想法。
在论坛上与我们分享您的反馈意见。作为 Tech from the Trenches系列的一部分,请务必关注其他 Unity 开发人员的新技术博客。
