UDN
Search public documentation:

MemoryDebuggingCH
English Translation
日本語訳
한국어

Interested in the Unreal Engine?
Visit the Unreal Technology site.

Looking for jobs and company info?
Check out the Epic games site.

Questions about support via UDN?
Contact the UDN Staff

UE3 主页 > 内存系统 > 内存调试

内存调试


内存调试概述


本部分帮助您理解如何调查和处理内存问题–尤其是对于控制台游戏平台来说,意味内存的应用受到内存的物理大小的限制。

由于不进行内存预算、使用巨大的资源、代码bug等情况,导致这些内存问题在游戏开发过程中是经常发生的。

这里将会提供一些处理这些情况中大多数问题的基本思想,但是请参照针对特定平台的问题获得更多的信息。

调试内存: 步骤


1. 加载入戏

如果您由于内存不足而不能加载游戏,请参照关卡优化页面来学习如何优化关卡。

通常有一个已定的内存预算指导方针是个好主意–比如,在关卡中可以使用多少静态网格物体;或者使用多少其它的资源比如骨架网格物体、动画、声效。这些都可以根据您做的是什么游戏以及它们是关于什么内容的而动态地变化。

2. 运行游戏并观察

即使游戏运行了,也不意味着您没有用光内存的危险。您需要知道您的游戏正在使用多少内存以及剩余有多少可用内存最好,这样来避免过量地使用它。

平台提供者有一些工具来完成这个功能。请参照下面的平台部分。

3. 降低内存使用量(没有OOM)

当您将要用光内存时,您需要尽量地把它们要用的内存量降低。

一些可能性的包括:

  • 具有太多静态网格物体的关卡。
  • 使用了太多射弹和粒子的关卡。
  • 具有占用了太多内存的代码。

基本理解

关于在各个方面应该使用多少内存没有通用答案。

这需要根据您的游戏及您游戏要注重花费内存的地方进行决定。

所以您需要理解您当前的应用是什么地方以并且您需要找到正确的内存预算。

  • Budget(预算):决定把内存应用在哪里
  • 确保所有的资源都是最佳的,并且没有任何不必要的引用。
  • 确保您有足够的缓冲(用于碎片)来保持您游戏的运行状态。

案例参考

           #define KEEP_TRACK_OF_NOVODEX_ALLOCATIONS 1

案例参考


关卡优化

Stats(统计数据)

STAT LEVELS

运行 Stat levels 命令来查看内存中加载了多少关卡。

  • Green(绿色):未加载。
  • Red(红色):已加载(以及加载所花费的时间)。
  • Blue(蓝色):未加载并进行了垃圾回收。
  • Yellow(黄色):已加载但不可见。
  • Pink(粉红色):准备加载。

如果你有一些不必加载的关卡,那么请一定不要加载它。如果可能,黄色的关卡是不需要进行加载及推迟加载的最好备选项。如果您有一个大的动态载入的关卡,请尝试把它进行分隔或者进行优化。

STAT MEMORY:

Stat Memory 提供了关卡中资源的内存应用的基本概况。

  • Audio Memory(音频内存)
  • Novodex Allocation(Novodex内存分配)
  • 动画
  • Vertex Lighting(顶点光照)
  • StaticMesh Vertex/Index(静态网格物体 顶点/索引)
  • SkeletalMesh Vertex/Index(骨架网格物体 顶点/索引)
  • Decal Vertex/Index(Decal 顶点/索引)
  • VertexShader(顶点着色器)
  • PixelShader(像素着色器)
  • Texture Pool Size(贴图池大小)
  • FaceFX

在很多情况下,由于虚幻参考系统的存在,这些中的很多项比如Animation(动画)、SkeletalMesh(骨架网格物体)、Audio(音频)以及FaceFX都是和游戏数据相互关联的。本文上好会对这方面的内容进行详细解释。

其它有用的控制台命令

  • ListSpawnedActors
  • ListLoadedPackages
  • ListPrecacheMapPackages
  • OBJ BULK

请参照关卡优化页面获得关于优化的更多信息;参照统计数据描述页面获得关于统计数据描述的更多信息。

引擎配置

Texture Pool Size(贴图池大小):

除了着色器目标之外的所有贴图都使用贴图池。这包括光照贴图。(使用光照贴图而不是顶点光照在内存节约上肯定是个胜利,因为它们将不会使系统内存发生变动。

同时,请确保您的游戏具有理想的贴图池大小。 Stat Memory 将会显示正在使用多少贴图池。基于那些数据,您将可以决定您游戏所要使用的贴图池的最佳大小。

在文件 *Engine.ini 中,
      [TextureStreaming]
         PoolSize=120

频繁的垃圾回收:

如果您的游戏需要比较频繁的垃圾回收,请使用这个配置。

在文件 *Engine.ini 中,
      [Engine.SeekFree]
         TimeBetweenPurgingPendingKillObjects=10 (every 10 seconds)

动画优化

当一个动画集被游戏引用时,那个动画集中的所有动画都将被加载。如果您仅使用这些动画中的一部分,那么这个过程是浪费的。如果动画集中的所有动画不是全部需要的,那么一般推荐您把它们分隔为子动画集,然后仅当需要时加载它们。

比如,除非您在任何时候都需要所有的武器,否则不同的武器可以有它们自己的动画集。

Matinee:

Matinee出现问题时,问题可能是严重的。

根据您的工作流程(也就是不同的人操作不同的Matinee序列)不同,基于每个Matinee序列或者每个关卡来维护不同的AnimSets(动画集)可能是非常复杂的。理想情况下,您会想在每个关卡中有一个AnimSet(动画集),但是在不同的开发组(关卡设计人员/动画制作人员/脚本人员)之间维护这些是非常耗费时间的。

在这种情况下,InterpData中的标志 bShouldBakeAndPrune (CL 259515) 可以帮助解决这个问题。它将为关卡创建一个新的动画集,并且仅复制那个关卡所使用的动画并重新链接引用。如果要 烘焙/加载 动画集,那么这个标志都应该设为off,因为它将会复制同样的动画。如果您正在烘焙及剪辑的动画集不是必须被关卡加载时则需要使用这个标志。这个标志可以通过设置 *Editor.ini 文件的Cooker.MatineeOptions部分的bBakeAndPruneDuringCook值为FALSE来全局地进行禁用。

In *Editor.ini,
      [Cooker.MatineeOptions]
         bBakeAndPruneDuringCook=false

动画压缩:

动画压缩是节约动画内存的很重要的一部分。请参照动画压缩页面获得更多信息。

参考系统

当游戏关卡已经接近完成时,对所有关卡进行一遍加载来确保它们仅加载了需要的资源是值得的。在虚幻引擎3中,任何被引用的内容都会被加载到游戏中,而且引用内容的方法–包括直接地和间接地引用等–这样您最终便会不知不觉地加载了很多的资源。比如,一个Pawn引用一个骨架网格物体,它将会自动地加载那个骨架网格物体所引用的动画集,从而那个动画集的所有动画(Pawn->Skeletalmesh->Animsets->Animations)将会被加载。从内存方面来说,这个链接关系是很危险的,因为游戏有很多Actors之间彼此进行着引用。

一般,您都必须要和关卡设计人员协同工作来决定是否有一些东西不必进行加载或者是否要对动态载入关卡进行分割以及是否不需要同时加载所有关卡。其中的一个实例许是在这个关卡中不需要出现的敌人网格物体。

MEMLEAKCHECK:

Memleakcheck 打印出许多基本游戏数据到一个文本格式的输出文件 [ProfileDirectory:Platform-specific]/MemLeak 中。

这将会至少为您显示骨架网格物体、动画集、声效等物体列表的简单概括信息。一旦您有了基本的信息,您便可以仔细地检查它们来查看是否有不需要加载的资源。如果是这样的,那么可以跟踪查出是谁引用了它,然后删除那个引用是主要的解决方式。

以下是案例学习...

OBJ LIST CLASS=SKELETALMESH:

列出了所有加载的SkeletalMeshes(骨架网格物体),按照占用内存的大小进行排序。 MEMLEAKCHECK 包含这个信息,但是它是按照字母顺序排序的,这使得您很难找到要进行优化的内容。

LISTSOUNDS:

列出了所有加载的SoundCues,按照占用内存的大小进行排序。 MEMLEAKCHECK 包含这个信息,但是它是按照字母顺序排序的,这使得您很难找到要进行优化的内容。

OBJ LIST CLASS=STATICMESH: 列出了所有加载的静态网格物体,按照占用内存的大小进行排序。

LISTANIMSETS:

列出了所有加载的SoundCues,按照占用内存的大小进行排序。 MEMLEAKCHECK 包含这个信息。

OBJ REFS:

用来查找为什么加载某个资源的最好用的方法是使用 OBJ REFS 命令。要想使用这个命令,您需要在PC上运行游戏,因为由于递归函数的存在它需要一个较深的栈,而在UE3的游戏控制台平台版本上通常会崩溃的。

播放到加载有问题的资源的地方,然后输入: OBJ REFS CLASS= NAME=

比如:

OBJ REFS CLASS=SKELETALMESH NAME=BIG_OGRE_2

然后它将会显示引入这个资源的引用物体链,比如:

Log: Shortest reachability from root to SkeletalMesh COG_Bolo_Grenade.Frag_Grenade:
Log:    SkeletalMesh COG_Bolo_Grenade.Frag_Grenade [target] (root) (standalone)
Log:    SkeletalMeshComponent GearGame.Default__GearProj_FragGrenade:COGGrenadeMesh0 (root) (ObjectProperty Engine.SkeletalMeshComponent:SkeletalMesh)
Log:    Class GearGame.GearProj_FragGrenade (root) (standalone)

在上面的实例中,Frag_Grenade网格物体由native 类GearProj_FragGrenade的默认属性所引用。

注意可能会有不止一个引用,在这种情况下您必须一个一个地处理它们。

这里是一些我们找到的没有正确地加载资源的原因:

  • 资源被native类引用。
  • UnrealScript代码引用这些需要从中获得默认属性的类(该类引用了资源)。
                      Asset = class'MyGameContent.Pawn_BigOgre'.default.Mesh.PhysicsAsset;
  • A Touch Kismet event (SeqEvt_Touch) references the class in its ClassProximityTypes or IgnoredClassProximityTypes arrays. Kismet的Touch事件(SeqEvt_Touch)引用它的ClassProximityTypes或IgnoredClassProximityTypes数组中的类。

内存碎片

MEM STAT: 仅是概要总结。

MEM DETAILED:

这将会输出关于分配器的有用信息。请参照下面的平台部分。

为内存碎片保存一些缓冲是个好主意。

栈内存

主线程继承于栈内存。尽管您可以指定您创建的线程的栈大小, 但如果您在内部正在使用一个创建线程的库,那么它可能不会考虑那个内存。

参考指南


以下页面提供了关于内存信息的详细信息。

游戏控制台平台的内存


有用的命令行命令