现有的各种工具和调试技术对于跟踪您游戏的内存泄漏问题和帮助查找内存分配状况趋势都是非常有用的。其中有一个工具是内存分析器。它被分割为两部分独立的代码:引擎中的C++后端和用于查看数据的C#前端。
要想获得其它工具,请参照内存跟踪页面。
在引擎侧,存储器分配分析器是一个在基本的默认分配器之前插入的代理分配器。它捕获对Malloc/ Free/ Realloc函数的调用,当调用发生时,它会序列化每次对磁盘进行分配内存和释放内存操作的相关信息。对于Malloc/ Realloc函数来说,不管执行捕获动作的内部函数是什么,这包含一个完整的调用栈, 同时还会捕获脚本调用栈。在 Playstation3 上,malloc 分析器会捕获在 "Local"(也就是 VRAM、RSX 内存)和 "Host"(可以访问 GPU 的主要内存)上分配的内存。
为了提高效率并降低文件的内存占有量,每次分配动作产生的标记流包含了到特定调用栈的索引,这使得即使捕获很长的游戏会话中所有的分配动作的性能消耗也是非常小的。(比如,加载战争机器2中的菜单关卡,它包含了引擎初始化、启动把序列化操作,这会导致产生一个大小约50 MB字节的文件。) 它支持自动把文件分割成大约1G字节的块,从而遭遇受到文件系统和引擎限制的问题。这样的设计会使得所捕获的信息的总量对前端的内存消耗仅造成较小的影响。
请访问内存分析器 PS3了解针对平台的下载章节。
您可以在//depot/UnrealEngine3/Binaries文件夹中找到C#可视化前端,我们称其为MemoryProfiler2.exe。它支持把分配流解析为真正的自顶向下的调用图表,把数据导出到CSV文件中,并且当在控制台输入 SNAPSHOTMEMORY
时,会生成C++后端嵌入的快照的差别。
MPROF STOP
命令并将所有需要的文件复制到目标位置后会自动在 Xbox360 上执行所有操作。MPROF STOP
后,这个文件夹中将会生成 mprof 文件。接下来您应该在这里复制一个 PS3 可执行文件,使用它可以收集统计数据。这个步骤非常重要,因为位于可执行函数中的符号通常会改变,将可执行函数随 mprof 文件一起存储是一个好的习惯。* 在控制台输入 **`MPROF MARK`** 来忽略快照标记。 这些是游戏运行时的内存查看器可以向您显示当前的内存分配状况的时候。 您还可以在标记后面紧接着会显示在 MemoryProfiler2 中的名称,例如, **`MPROF MARK AtMainMenu`** ( **`SNAPSHOTMEMORY`** 也可以代替 **`MPROF MARK`** )。* 此类屏幕截图将会被标记为 **"Snapshot <Index>: <Text>"**
* 输入 **`MPROF STOP`** 来完成优化会话,并序列化头文件信息到一个内存分析(.mprof)文件中。当输入这个命令后将不能进行进一步的捕获跟踪。 注意: 如果没有进行这步操作,那么捕获跟踪就是没有用的,通常在您完成捕获您所感兴趣的区域时输入 **`MPROF STOP`** ( **`DUMPALLOCSTOFILE`** 是 **`MPROF STOP`** 的另一个名字)。
* 引擎还会插入一些自动的屏幕截图,这个列表包括以下内容* 当引擎在加载一个新的关卡之前开始清理过程的时候截取的屏幕截图,将会被标记为 **"LoadMap Start: <LevelName>"*** 当新关卡开始加载后截取的屏幕截图,将会被标记为 **"LoadMap Mid: <LevelName>"*** 当新关卡加载完成后截取的屏幕截图,将会被标记为 **"LoadMap End: <LevelName>"*** 当垃圾回收开始的时候截取的屏幕截图,将会被标记为 **"GC Start: <Index>"*** 当垃圾回收完成的时候截取的屏幕截图,将会被标记为 **"GC End: <Index>"*** 当申请加载一个新的动态加载关卡加载后截取的屏幕截图,将会被标记为 **"LevelStream Start <Index>: <LevelName>"*** 在使之前动态加载的关卡可见后截取的屏幕截图,将会被标记为 **"LevelStream End <Index>: <LevelName>"**
将这个章节划分为三个部分。
![02FileMenu.png](02FileMenu.png) | **文件** * **Open (F2)** - 会创建一个打开对话框来选择 .mprof 文件 * **Export to CSV (Ctrl+E)** - 会将分配从屏幕截图中导出到一个 CSV 文件,通过名字传递 * **Quit (Ctrl+X)** - 会关闭内存分析器应用程序 |
![03EditMenu.png](03EditMenu.png) | **编辑** * **Find (Ctr+F)** - 会打开查找对话框,只在调用图表试图中有效,请参阅下面的 **[Find dialog](#FindDialog)** * **Find next (F3)** - 会根据查找对话框中的搜索标准查找下一个匹配项。 * **Copy highlighted to clipboard** - 会将调用图表视图中高亮显示的节点复制到剪贴板中,只有在 **"Find all(查找全部)"** 后使用时才有效 * **Unhighlight all** - 会取消高亮显示调用图表视图中的所有节点 |
![04ToolsMenu.png](04ToolsMenu.png) | **工具** * **Options (Ctrl+O)** - 会打开选项对话框,请参阅下面的 **[选项对话框](#OptionsDialog)** * **Invert callgraph view (Ctrl+I)** - 默认情况下会禁用这个选项,请参阅下面的[调用图表视图](#CallgraphView)部分 * **Filter out Object VM functions (Ctrl+V)** - 是否过滤掉所有与 UObject 虚拟机器相关的函数,例如, **UObject::exec** 、 **UObject::CallFunction** 或 **UObject::ProcessEvent** ,默认情况下处于启用状态。要应用这些更改,您必须重新加载文件。请参阅下面的[调用图表视图](#CallgraphView)部分 |
![05HelpMenu.png](05HelpMenu.png) | **帮助** * **Open UDN help page (F1)** - 在浏览器中打开这个页面 |
在选项对话框中,只有一个设置可以进行编辑。这是 ClassGroups 集合,一个定义调用栈过滤的类组的列表。它可以支持常规的表达式,所以设置一个单独的可以缓存您感兴趣的各种调用栈的表达式。将选项存储到文件 MemoryProfiler2.ClassGroups.xml 中,这样您就可以通过使用内置编辑器或手动编辑它。每次关闭内存分析器应用程序的时候都会写入选项。在您打开了一些应用程序实例的情况下要小心。
每个类组都包含下面的属性:
CallstackPattern 包含一些其他属性:
所有列出来的模式必须与要匹配的模式的调用栈中的栈帧匹配。 栈帧必须与帧模式的顺序一致,但是它们不需要是连续的。 例如,考虑这个 Pattern(模式): {"A::Function1()", "B::Function2()"} 下面的调用栈与它匹配: Blah() A::Function1() Blah2() Blah3() B::Function2() Blah4() 下面的调用栈不匹配: B::Function2() A::Function1() nor does this: Blah() A::Function1() Blah2() 最多调用栈模式将只会有一个单独的帧模式,但是允许多个帧可以更加灵活。 |
有些数据结构会占据大量内存。这里有一个隐藏的选项对话框,它可以让您禁用特定功能以保存内存。ProfilingOption 会调用这个对话框,现在通过 GUI 无法访问它,默认情况下它会启用所有选项。如果您发现这个内存分析器应用程序在加载一个大的概要文件时内存溢出,那么您应该试着更改选项或者使它可以通过 GUI 使用。
在非独占视图、柱状图视图、内存位图视图和短期内存分配视图中,其中列表视图包含用户可以选择一排然后只将选中的这一排全部调用栈复制到粘贴板中的调用栈。
" " 位于这个选项的旁边表示该选项已经更改过,您必须点击 Go(开始) 刷新数据。 对于某些视图一些过滤选项可能无法使用(灰掉了),例如在加载除 PS3 mprof 数据之外的任何数据时的 "Pool Filter(池过滤器)" 。
![08ToolDiffStart.png](08ToolDiffStart.png) | **比较开始** 这个组合框包含所有从 mprof 文件中读取的屏幕截图的数组。 用来选择比较的开始截图。 开始截图是一个已经完成的虚拟截图。 默认: **Start** |
![09ToolDiffEnd.png](09ToolDiffEnd.png) | **比较结束** 这个组合框包含所有从 mprof 文件中读取的屏幕截图的数组。 用来选择比较的结束截图。 默认: **End** |
![10ToolSortBy.png](10ToolSortBy.png) | **按照大小分类** 设置调用图表视图/独占视图中的节点的分类 * **Sort by Size** - 按照内存分配的大小对节点进行分类,默认情况下会启用这个选项 * **Sort by Count** - 按照内存分配的数量对节点进行分类 |
![11ToolAllocations.png](11ToolAllocations.png) | **激活的内存分配** 设置将用于在调用图表视图、独占视图和柱形视图中填入数据的指定调用栈列表 * **Active Allocations** - 设置激活的内存分配列表(这个包括只有在截图的结束部分才会提供内存分配),默认情况下会启用这个选项 * **Lifetime Allocations** - 设置声明周期内存分配列表(这包括在截图的开始和结束部分之间的所有内存分配,即使是已经被释放的内存) |
![12ToolContainers.png](12ToolContainers.png) | **隐藏 Container** 设置用于通过函数名称隐藏 container 的选项。将 container 定义为具有以下名称的函数: operator new<, operator<<, >::, FString::operator=, FStringNoInit::operator=, FString::FString, FBestFitAllocator::, FHeapAllocator:: * **Hide Containers** - 启用通过函数名称隐藏 container,默认情况下会启用这个选项 * **Show Containers** - 禁用通过函数名称隐藏 container |
![13ToolFiltering.png](13ToolFiltering.png) | **过滤进入** 设置过滤类型 * **Filter Out** - 根据过滤、文本过滤和池过滤的类启用过滤出来 * **Filter In** - 根据过滤、文本过滤和池过滤的类启用过滤进入,默认情况下会启用这个选项 |
![14ToolClassesToFilterSmall.png](14ToolClassesToFilterSmall.png) | **要过滤的类** 用于缩小结果范围的常用类组的列表。每次用户更改选项对话框中的 **ClassGroups** 集合时都会更新这个菜单。默认情况下会检查所有类。 * **All** - 会勾选所有类组 * **None** - 会取消勾选所有类组 |
![15ToolTextFilter.png](15ToolTextFilter.png) | **文本过滤** 启用通过键入的文本过滤数据,默认情况下为空。 键入一个文本启用基于文本的过滤功能。按下回车键或者点击 ![Play.png](Play.png) **Go(开始)** 刷新数据 / 开始进行处理。 |
![16ToolPoolFilter.png](16ToolPoolFilter.png) | **池过滤** __(这个选项只适用于 PS3)__ 会启用通过内存池进行过滤,默认情况下会勾选所有池 * **Main** - 主要内存池 * **Local (GCM)** - 本地视频内存池(RSX 内存) * **Host (Default)** - 主机内存池(RSX 可以访问的内存) * **Host (Movies)** - 用于存储视频的主机内存池 * **All** - 选择所有内存池 * **None** - 不选择任何内存池 |
![17ToolGo.png](17ToolGo.png) | **Go(开始)** 开始处理选中的截图和视图的数据 * ![Play.png](Play.png) **Go** - 当前截图是最新截图 * ![PlayRed.png](PlayRed.png) **Go** - 当前截图需要刷新数据,这个按钮只有在您在主工具条中更改了一些选项后才会改变 __已知问题:__ * __如果您切换到另一个选项卡,那么在刷新后 ![Play.png](Play.png) **Go** 按钮将仍然保持为绿色__ |
会显示由唯一的调用栈统计的内存分配情况。将视图分为两个树形结构,第一个树形结构包含删减的调用栈,第二个树形结构包含所有的调用栈。删减的调用栈通常会出现在那些调用栈很长的脚本调用栈上。沿着树结构向下的每个节点包含了内存分配的概要。
调用图表视图还具有搜索功能。用户可以使用与其他应用程序相似的功能进行搜索,包括常规的表达式支持以及一个可以突出显示您的搜索查询中的所有实例并通知您它们增加了多少内存的 Find All(查找全部) 函数。这样做,或者用户可以根据文本过滤获得相同的结果。
![06FindDialog.png](06FindDialog.png) | ![22FindAllResults.png](22FindAllResults.png) |
当您在节点上右击的时候,会显示关联菜单。这个菜单包含以下选项:
UGearEngine::Tick 的调用图表视图中包含可见的关联菜单。
用户现在可以在调用图表视图中倒置调用栈,这样它就会从上到下显示它们而不是从下到上地显示。这样做有时候有利于查找通过很多不同的地方调用的底层函数的内存分配情况。它的选项在 Tools(工具)菜单中, "Invert callgraph view(倒置调用图表视图)(Ctrl+I)" 。请参照下面的截图:
这是一个脚本调用栈的示例,其中启用了过滤出 UObject 虚拟机器函数的功能。勾选 "Tools Filter out Object VM functions (Ctrl+V)"
与上面的示例相同,但是禁用了过滤出 UObject 虚拟机器函数的功能。取消勾选 "Tools Filter out Object VM functions (Ctrl+V)"
会显示由组汇集的内存分配情况。这个视图由两个主要列表视图组成,左边是一个通过唯一调用栈分组的内存分配列表,右边是一个完整的调用栈信息,其中包含每一帧的文件名和行号。下面是有关左边的列表视图列标题的详细信息:
您可以通过在列标题上点击挑出一个特定的列。排列顺序由分类箭头指定。
这个视图会显示整个运行过程或者选中的截图的内存分配历史记录。只是一个呈栈形排列的图表,下面的表格将会对用户在图标上可以找到一些系列进行说明:
Allocated Memory(分配的内存) | 所有平台 | The total amount of memory used by the game(供游戏使用的内存的总量) |
---|---|---|
Image Size(图片大小) | Xbox360, PS3 | 加载的可执行函数的大小,栈、静态和全局对象大小 |
OS Overhead(OS 消耗) | Xbox360 | 操作系统的消耗 |
Virtual Used(使用的虚拟内存) | 所有平台 | 应用程序虚拟内存正在使用的内存分配 |
Virtual Slack(虚拟闲置) | 所有平台 | OS/分配器中的内存分配,但是不供应用程序使用 |
Virtual Waste(虚拟浪费) | 所有平台 | 掌控的分配器中的调整浪费,此外还会记录持续的消耗 |
Physical Used(使用的物理内存) | 所有平台 | 应用程序物理内存正在使用的内存分配 |
Physical Slack(物理闲置) | 所有平台 | OS 中的内存分配,但是不供应用程序使用 |
Physical Waste(物理浪费) | 所有平台 | 掌控的分配器中的调整浪费,此外还会记录持续的消耗 |
Host Used(使用的主机) | PS3 | 供应用程序使用的主机内存 |
Host Slack(主机闲置) | PS3 | 分配的主机内存,但是不可以供应用程序使用 |
Host Waste(主机浪费) | PS3 | 由于内存分配的调整而浪费的主机内存 |
用户还可以添加自定义标记点。要进行这项操作,您需要在您感兴趣的地方点击这个图片,然后只要重新加载了当前的 mprof 文件后,这些自定义标记点就会被添加到这个截图中,描述内容为 "Unnamed snapshot allocations: <AllocationCount>"
调用栈历史记录选项卡会显示调用栈内存分配的完整历史记录,甚至通过 reallocs 方法追踪它。例如,如果您具有一个名为 malloc 的调用栈,然后是另一个稍后会将同一个内存分配重新分配一个较大的尺寸的调用栈,在这种情况下如果您显示了第一个调用栈的历史记录,那么它会包括重新分配的内存,您将可以看到这个图表行在这一点上升,而不是下降到 0。您可以显示调用栈的整个组的历史记录,而且它会将所有图标累加在一起。它对于找到那些使用截图可能无法检查到的顿卡或者是侦查泄露非常有用(它看起来是一个不断攀升始终不会下降的行)。
注意,如果您想要查看多个调用栈的历史记录(独占视图、短期内存分配视图、内存位图视图),那么这个应用程序必须在可以显示历史记录之前将将它们所有的尺寸大小图标都累加在一起,这个过程可能需要几秒钟,如果调用栈很多的话,可能需要更长时间。
在这个应用程序中到处都是各种右击菜单,其中的 "View History(查看历史记录)" 选项可以让您查看特定的调用栈。参阅下面表格中每个支持的视图的示例,这样才能知道如何得到它们:
![40CallstackHistory_CallgraphView.png](40CallstackHistory_CallgraphView.png) | **[调用图表视图](#CallgraphView)** 它会显示从这个您右击菜单项 **"View History(查看历史记录)"** 的节点继承而来的所有调用栈的历史记录 |
![41CallstackHistory_ExclusiveView.png](41CallstackHistory_ExclusiveView.png) | **[独占视图](#ExclusiveView)** 您可以选择多个调用栈 |
![42CallstackHistory_Histogram.png](42CallstackHistory_Histogram.png) | **[柱形视图](#HistogramView)** 您可以从这个主菜单栏 (Local/Video) 或者从详细的菜单栏中选择一个栏。有时这些栏非常小,点击它们比较困难无法做到,所以您可以在 **"Classes to Filter(要过滤的类)"** 下拉列表中右击类组,这样就可以选中它们啦! |
![43CallstackHistory_MemoryBitmap.png](43CallstackHistory_MemoryBitmap.png) | **[内存位图视图](#MemoryBitmap)** 您可以选择内存区域,并且在这个区域内已经执行了多个内存分配,通过列表视图 **"Allocation History(内存分配历史记录)"** |
![44CallstackHistory_ShortLivedAllocation.png](44CallstackHistory_ShortLivedAllocation.png) | **[短期内存分配视图](#ShortLived)** 您可以选择多排 |
![45CallstackHistory_ClassesToFilter.png](45CallstackHistory_ClassesToFilter.png) | **要过滤的类** 您可以从现实的类组列表中选择一个类或使用 **"All(全部)"** 菜单选择全部,但是要显示所有调用栈的调用栈历史记录可能需要一个多小时的时间。 |
选择 "View history(查看历史记录)" 后,您将会看到下面的对话框,其中还会显示进度条。如果有时候您需要只是历史记录的开头部分,或者如果您意外关闭了"View History(查看历史记录)" ,您可以随时取消这项操作。
带有完全处理的图片的调用栈历史记录选项卡看起来像下面的画面一样,X 轴为内存分配的动态加载位置。绿色条 表示垃圾回收使用的时间。蓝色条 表示永久性关卡加载使用的时间。这个视图的根据菜单包含以下选项:
X 轴为内存分配的帧位置的图片。
柱形图选项卡会将两个内存条显示为列(实际上只有 PS3 有两个内存条,其他平台只使用第一个内存条)。将这两列划分为条,划分的一句是在选项对话框中定义的类组。当您点击一个条时,第三列将会填入在这一列中所有调用栈的细目。当您在这个新的列中点击一个条时,您可以在右边的列表框中看到这个调用栈,在"Details(详细信息)" 字段中看到一些信息。您可以在您的键盘上使用 "up(向上)" 和 "down(向下)" 箭头键在柱形条之间移动。
在前面两列中的底部条,填充的颜色为紫色,由取消组合的调用栈组成。对它使用特殊的 Ungrouped(取消组合) 的组合。如果该区域特别大 - 那意味着您可能需要分配它的某些调用栈来自定义组合。
内存位图选项卡是非常明显的。不推荐使用 Win64 概要文件,因为它们会在靠近空间底部的地方分配一些内存,然后在靠近空间顶部的地方分配一些,所以默认情况下,它最终实际上都是缩小的视图。最初它可能是空白的,但是如果您仔细看,您应该会在底部和顶部看到一些像素。32 位平台更加合情合理。点击这个位图会为您提供在这个地址上所有内存分配的完整历史记录,然后吐出显示最近的历史记录。
注意,图片底部的红紫色区域是内存空间结束的地方。位图中每个像素的字节数是正数,这个位图的结尾部分始终都会有一些超出所选内存范围的像素。将它们着色为深紫红色
这个视图的根据菜单包含以下选项:
短期内存分配会显示在进行分配后不久就被释放的内存分配,这可能会引发性能和碎片问题。下面是有关左边的列表视图列标题的详细信息:
这个过程需要一段时间,所以准备好在按下 Go(开始) 后要等待大约一分钟
您可以通过在列标题上点击挑出一个特定的列。排列顺序由分类箭头指定。
会显示所选屏幕截图标记的详细信息。Start(开始) 会显示开始屏幕截图中的统计数据, End(结束) 会显示结束屏幕截图中的统计数据,而 Diff(区别)会显示 End(结束)-Start(开始) 之间的区别。测量值项中的大多数统计数据只受 PS3 上的 mprof 文件支持。一些其他元素也可能是 0,这种情况会在使用了没有统计数据的 Test(测试)版本采集数据时发生。
已知问题:
版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。
工作时间:8:00-18:00
客服电话
电子邮件
admin@qq.com
扫码二维码
获取最新动态