虚幻引擎MemReport工具使用笔记

  • 虚幻引擎可以用指令MemReport -full来输出一份内存报告。生成的内存报告路径一般在:`Project\Saved\Profiling\MemReports\ 下。
  • 这份报告里我们可以看到虚幻引擎各种资源在内存里的大小,以及部分资源信息,例如:贴图大小、贴图格式等。
  • 对于很多资源我们可以看到这样的格式: 在代码里面,这些数据是这样输出的:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    ...
    if (bCSV)
    {
    Ar.Logf(TEXT(",Object,NumKB,MaxKB,ResExcKB,ResExcDedSysKB,ResExcDedVidKB,ResExcUnkKB"));
    }
    else
    {
    Ar.Logf(
    TEXT("%140s %10s %10s %10s %15s %15s %15s"),
    TEXT("Object"),
    TEXT("NumKB"),
    TEXT("MaxKB"),
    TEXT("ResExcKB"),
    TEXT("ResExcDedSysKB"),
    TEXT("ResExcDedVidKB"),
    TEXT("ResExcUnkKB")
    );}

    for (const FSubItem& ObjItem : Objects)
    {
    if (bCSV)
    { Ar.Logf(
    TEXT(",%s,%.2f,%.2f,%.2f,%.2f,%.2f,%.2f"),
    *ObjItem.Object->GetFullName(),
    ObjItem.Num / 1024.0f,
    ObjItem.Max / 1024.0f,
    ObjItem.TrueResourceSize.GetTotalMemoryBytes() / 1024.0f,
    ObjItem.TrueResourceSize.GetDedicatedSystemMemoryBytes() / 1024.0f,
    ObjItem.TrueResourceSize.GetDedicatedVideoMemoryBytes() / 1024.0f,
    ObjItem.TrueResourceSize.GetUnknownMemoryBytes() / 1024.0f
    );

    } else
    {
    Ar.Logf(
    TEXT("%140s %10.2f %10.2f %10.2f %15.2f %15.2f %15.2f"),
    *ObjItem.Object->GetFullName(),
    ObjItem.Num / 1024.0f,
    ObjItem.Max / 1024.0f,
    ObjItem.TrueResourceSize.GetTotalMemoryBytes() / 1024.0f,
    ObjItem.TrueResourceSize.GetDedicatedSystemMemoryBytes() / 1024.0f,
    ObjItem.TrueResourceSize.GetDedicatedVideoMemoryBytes() / 1024.0f,
    ObjItem.TrueResourceSize.GetUnknownMemoryBytes() / 1024.0f
    );

    if (bVerboseObjectOutput)
    { ObjItem.TrueResourceSize.LogSummary(Ar);
    } }}
    ...
    从代码里面不难看出后四列的含义:总内存占用、专用系统内存占用、专用显存占用、未知的内存占用。但是前两列的含义就不清晰了。
    继续追踪代码,实际上ObjItem关于NumKB和MaxKB的内存信息是由类FArchiveCountMem收集的。观察FArchiveCountMem的实现:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    	class FArchiveCountMem : public FArchiveUObject  
    {
    public:
    FArchiveCountMem( UObject* Src, bool bFilterEditorOnly = false )
    : Num(0)
    , Max(0)
    { ArIsCountingMemory = true;
    ArIsFilterEditorOnly = bFilterEditorOnly;
    if( Src )
    { Src->Serialize( *this );
    } } SIZE_T GetNum() const
    {
    return Num;
    } SIZE_T GetMax() const
    {
    return Max;
    }
    virtual void CountBytes( SIZE_T InNum, SIZE_T InMax ) override
    {
    Num += InNum;
    Max += InMax;
    } /**
    * Returns the name of the Archive. Useful for getting the name of the package a struct or object * is in when a loading error occurs. * * This is overridden for the specific Archive Types **/ virtual FString GetArchiveName() const override { return TEXT("FArchiveCountMem"); }

    protected:
    SIZE_T Num, Max;
    };
    不难看出一般是在UObject的某个和序列化相关的函数中统计的
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    void UObject::Serialize(FStructuredArchive::FRecord Record)  
    {
    ...
    // Memory counting (with proper alignment to match C++)
    SIZE_T Size = GetClass()->GetStructureSize();
    UnderlyingArchive.CountBytes(Size, Size);
    ...
    }

    /** Returns actual allocated size of structure in memory */
    FORCEINLINE int32 GetStructureSize() const
    {
    return Align(PropertiesSize,MinAlignment);
    }

    至此,我们可以得出结论:一般来说,NumKB和MaxKB的内存信息是统计被UPROPERTY宏标记的成员占用的内存
    另外的情况就是涉及到网络或者Serialize(FStructuredArchive::FRecord Record)被重写的情况,通过查找CountBytes的引用,发现很多涉及到网络相关数据大小的统计也用到了这玩意。因此,具体含义可能需要具体分析,但一般情况可以默认为被UPROPERTY宏标记的成员占用的内存。