在 虚幻引擎5(UE5)中,Unreal Insights 更加强大,其 Memory Insights 功能中添加了更强的内存追踪和分析功能。开发者现在可以查看有关内存分配和释放的更多详细信息,包括在任何时间点与每个内存块关联的 低级内存(LLM)标签 和调用堆栈。 Memory Insights有一个查询系统,可以查找某个时间点的实时分配,识别内存使用量的增加或减少,区分短期和长期分配,以及查找内存泄漏。
记录会话
要开始使用Memory Insights在内存通道中记录追踪(Trace),请按照以下步骤操作:
运行或构建Unreal Insights
导航到 Start(开始) > 命令提示符(Command Prompt),然后输入以下内容:
Engine\Binaries\Win64\UnrealInsights.exe
或者,也可以导航到 Engine\Binaries\Win64
文件夹并双击运行 UnrealInsights.exe
。

使用内存追踪运行你的游戏项目
从操作系统启动 命令提示符(Command Prompt) 并运行你的项目示例:
cd C:\MyEngineInstallLocation\
Samples\Games\MyGameSample\Binaries\Win64\MyGameSample.exe -trace=default,memory
要记录项目的会话,内存追踪通道从流程开始就必须处于激活状态。否则,无法在延迟连接的会话中开始追踪分配事件。此外,如果要对打包的项目运行追踪,则需要确保项目是在 开发(Development) 模式下打包的。
从Insights会话浏览器中打开追踪
导航回Unreal Insights会话浏览器(Unreal Insights Session Browser),然后双击你的.utrace文件以在虚幻引擎的 Timing Insights 窗口中将其打开进行分析。选择 Menu(菜单) > Memory Insights 以打开Memory Insights窗口。

内存分配 - 图表追踪
Unreal Insights会为每个分配事件捕获完整的调用堆栈,从而提供项目内存分配情况的分析。
Memory Insights 的主界面包含一个时间轴,显示会话期间内存使用情况的概览。

Memory Insights追踪器显示有关内存中实时分配数量的信息。 上图是主内存图表(Main Memory Graph)以及实时分配计数(Live Allocation Count)和空闲事件计数(Free Event Count)。
Unreal Insights会为每个分配事件捕获完整的调用堆栈,从而提供项目内存分配情况的分析。
主内存图表(Main Memory Graph) 显示项目中追踪的内存总量,包括从 LLM 收集的每个标签的信息。此外,还有显示实时分配总数的图表:
图表类型 | 颜色 | 描述 |
---|---|---|
分配的总内存(Total Allocated Memory) | 蓝色 | 根据详细的分配追踪信息显示在每个时间点分配的内存总量。 |
实时分配计数(Live Allocation Count) | 黄色 | 显示任何时间点的活跃分配总数。 |
分配/空闲事件计数(Allocation/Free Event Count) | 绿色/蓝色 | 显示每个单元的分配和空闲事件数,以时间"切片"表示。 |
这些图表中的每一个都基于详细的分配追踪信息。它们从时间值0开始,粒度约为1ms。其他带有LLM前缀标签(RenderTargets、SceneRender、UObject)的图表基于低级内存追踪运行时系统。
这些标签将在会话开始几秒钟后开始追踪,并包含每帧粒度。
默认情况下,我们在每4096个分配/空闲事件中发出一个时间戳,如果需要,可以更改此数量,方法是修改位于Engine/Source/Runtime/Core/Private/ProfilingDebugging/MemoryAllocationTrace.cpp中的MarkerSamplePeriod。 例如,如果将此变量设置为值0,则会在每个分配/空闲事件之后发出一个时间戳。
Memory Insights时间轴支持将追踪叠加到 时间视图(Timing View) 上。 使用Memory Insights视图时,有四个可用的面板,即 时间(Timing)、调查(Investigation)、LLM标签(LLM Tags) 和 模块(Modules) 面板。
时间视图
可以单击 时间(Timing) 来切换 时间视图(Timing View) 窗口的显示。 在此窗口中可以观察和过滤与内存使用相关的不同追踪的性能数据。

调查面板
在 调查(Investigation) 面板中可进行与分配相关的不同查询。

低级内存(LLM)标签
LLM标签(LLM Tags) 面板用于控制不同LLM标签的可视性。此数据直接从操作系统中追踪。

模块
解析调用堆栈符号时,结果存储在缓存文件中。可以通过单击 模块(Modules) 来查看这些文件。在此面板中可打开旧的追踪文件并使用符号。

以前的内存追踪运行时实现方案是在位于文件夹Engine\Source\Runtime\Core\Public\HAL\LowLevelMemTracker.h中的 LowLevelMemTracker 类中实现的。"LLM标签(LLM Tags)"面板和LLM图表都使用直接从该系统中追踪到的数据。详细的分配数据来自单独且特定的追踪实现方案。
Memory Insights包含新的查询功能和追踪的内存分配信息。你可以识别出UE5在特定时间窗口内、特定时刻之前或之后分配和释放的内存块,或检查内存泄漏。在打开追踪日志后转到 调查(Investigation) 选项卡即可访问查询系统。
调查 - 分配查询
虽然时间轴提供了内存使用情况的概览,但还是需要通过使用"查询"来评估具体分配在一段时间内的表现。一次 查询(Query) 由一条 规则(Rule) 和一个或多个 时间戳(Timestamp) 进行定义,例如标签 A 和 B。

"调查(Investigation)"面板 包含用于评估数据的 查询 规则。
可用的 查询规则 如下:
查询规则 | 时间变量 | 描述 |
---|---|---|
活跃分配(Active Alloc) | A | 显示时间A的所有活跃分配。 |
之前(Before) | A | 显示时间A之前的所有分配。 |
之后(After) | A | 显示时间A之后的所有分配。 |
下降(Decline) | A和B | 显示在时间A之前分配并在时间A和时间B之间释放的所有分配。 |
增长(Growth) | A和B | 显示在时间A和B之间分配并在时间B之后释放的所有分配。 |
增长与下降(Growth Vs Decline) | A和B | 表示"增长"分配(在时间A和时间B之间分配并在时间B之后释放)和"下降"分配(在时间A之前分配并在时间A和时间B之间释放)。 下降分配会更改为具有负值大小,因此大小聚合后显示A和B之间的变化。 此查询的结果是将时间A的分配与时间B的分配进行比较的结果。 按标签或调用堆栈对内存分配进行分组将显示每个组的变化(B - A)。 |
空闲事件(Free Events) | A和B | 显示在时间A和B之间释放的所有分配。 |
分配事件(Alloc Events) | A和B | 显示在时间A和B之间分配的所有分配。 |
短期分配(Short Living Allocs) | A和B | 显示在时间A之后分配并在时间B之前释放的所有分配。此规则可用于识别可能是堆栈分配的分配,也就是识别临时或短期分配。 |
长期分配(Long Living Allocs) | A和B | 显示在时间A之前分配并在时间B之后释放的所有分配。 |
内存泄漏(Memory Leaks) | A、B和C | 显示在时间A和B之间分配并且直到时间C之后才释放的所有分配。此规则可用于查找预期会在给定时间(例如在关卡转换期间)释放的内存。 |
有限的生命周期(Limited Lifetime) | A、B和C | 显示在时间A和B之间分配并在时间B和C之间释放的所有分配。 |
长期分配下降(Decline of Long Living Allocs) | A、B和C | 显示在时间A之前分配并在时间B和时间C之间释放的所有分配。 |
特定生命周期(Specific Lifetime) | A、B、C和D | 显示在时间A和B之间分配并在时间C和D之间释放的所有分配。 |
要进行查询,可选择一条规则,然后将时间轴中的标签标识拖动到所需位置,或在 调查(Investigation) 选项卡中指定时间。

选择所需的规则和时间后,请点击"调查(Investigation)"选项卡中的 运行查询(Run Query) 按钮进行查询。

根据具体查询和正在捕获的数据集,查询可能需要相当长的执行时间。
分配明细视图
运行查询时,会出现一个新窗口,而完成查询后,会在分配(Alloc)表中填充结果。默认情况下,这些结果将显示在平面列表中。
每个分配都显示有 计数(Count)、大小(Size)、LLM 标签(LLM Tag) 和进行分配的 函数(Function)。

将鼠标悬停在函数或信息图标上将显示更多详细信息和完整的调用堆栈。

将鼠标悬停在信息图标上时会显示完整的调用堆栈。
排序
通过单击表格标题,可以按不同的列对列表进行排序。

排序列 | 描述 |
---|---|
分配层级(Allocation Hierarchy) | 按分配树的层级排序。 |
分配计数(Allocation Count) | 按分配的数量排序。 |
大小(Size) | 按分配的大小排序。 |
LLM标签(LLM Tag) | 按分配的LLM标签排序。 |
函数(调用堆栈)(Function (Callstack)) | 按分配的调用堆栈中已解析的顶部函数排序。 |
分组
默认(Default) | 显示默认分配。 |
详细(Detailed) | 配置树状图以显示详细的分配信息。 |
堆(Heap) | 调查不同内存类型的使用情况。请参阅多个地址空间。 |
大小(Size) | 快速查找所有大型分配。 |
标签(Tags) | 显示每个系统的分配。 |
调用堆栈(Callstack) | 调查分配来自哪个调用堆栈。 |
反向调用堆栈(Inverted Callstack) | 配置树状图以按反向调用堆栈显示分配明细。 |
地址(4k页)(Address(4k Page)) | 根据地址将分配分组到4k对齐的内存页中。
|
可以使用 预设(Preset) 选项将分配分组到一起。

导航到 层级(Hierarchy),然后单击 全部(All) 将打开一个下拉菜单,可将默认 平面视图(Flat View) 更改为其他替代组。

高级过滤
搜索文本框提供了一种基于分层节点文本快速过滤结果的方法。单击搜索文本框旁边的 过滤配置器(Filter Configurator),可以进一步过滤查询产生的分配集以便得到一组分配。

还可以使用组和AND/OR关键字构建高级查询。
调用堆栈符号解析
项目中的 调用堆栈符号追踪(Call Stack Symbol Tracing) 是使用程序计数器地址完成的。在分析时,需要将这些地址解析为可读字符串以及关于相应源文件的信息。
这就要求Memory Insights能够访问包含调试信息的文件的正确版本:.pdb
或 .elf
文件(取决于平台)。Insights将根据以下列表搜索正确的文件:
-
用户在此会话中输入的任何新路径。
-
可执行文件的路径(如果在某些平台上可用,它将被编译成二进制文件)。
-
来自
UE_INSIGHTS_SYMBOLPATH
环境变量的路径。此变量接受分号分隔的路径。 -
来自用户配置文件的路径。
解析符号后,结果将存储在缓存文件中。可以通过单击 模块(Modules) 面板查看这些文件。然后,可以通过右键单击所选文件并从下拉菜单中选择以下选项之一来打开这些文件。

因此,你可以直接将追踪文件发送给其他用户,而不必要求这些用户具备访问该调试信息的权限。
加载方法 | 描述 |
---|---|
从文件加载符号 | 通过指定文件来加载模块的符号。如果成功,则会尝试从同一目录加载其他失败的模块。 |
从目录加载符号 | 通过指定目录来加载模块的符号。如果成功,则会尝试从同一目录加载其他失败的模块。 |
多个地址空间
内存追踪功能可追踪不同 堆 中的内存。从概念上讲,任何分配都必须属于 根堆(代表一种内存)。例如,在桌面平台上,一个根堆是系统内存,另一个根堆是显卡的显存。每个根堆都有自己的地址空间。 在每个根堆下进行可以托管分配的堆分配。通常,这种分配表示支持分配的虚拟内存分配,但是,块式分配器也可以用堆分配来表示。这样就可以调查这些内存块的使用情况。