UObject创建系统深度分析


文章目录


1. StaticConstructObject_Internal - 顶层构造管理器

核心职责

作为UObject创建系统的入口点,负责协调整个创建流程。

关键逻辑分析

1.1 前置验证和安全检查

cpp 复制代码
// 编辑器专用检查:防止在序列化过程中创建对象
#if WITH_EDITORONLY_DATA
bool bCanConstruct = InOuter == nullptr || !UE::IsSavingPackage(...);
UE_CLOG(!bCanConstruct, LogUObjectGlobals, Fatal, 
        TEXT("Illegal call while serializing object data!"));
#endif

// 模板对象类型验证
checkf(!InTemplate || InTemplate->IsA(InClass) || (InFlags & RF_ClassDefaultObject), 
       TEXT("Template must be an instance of the class"));

1.2 子对象复用判定系统

cpp 复制代码
const bool bIsNativeClass = InClass->HasAnyClassFlags(CLASS_Native | CLASS_Intrinsic);
const bool bIsNativeFromCDO = bIsNativeClass &&
    (!InTemplate || (InName != NAME_None && 
     (Params.bAssumeTemplateIsArchetype || 
      InTemplate == UObject::GetArchetypeFromRequiredInfo(...))));

const bool bCanRecycleSubobjects = bIsNativeFromCDO && 
    (!(InFlags & RF_DefaultSubObject) || 
     !FUObjectThreadContext::Get().IsInConstructor);

复用条件解析:

  1. 必须为原生类(CLASS_Native或CLASS_Intrinsic)
  2. 必须从CDO派生或使用正确的原型
  3. 不能是构造函数中的默认子对象(除非特殊标记)

1.3 对象分配调用

cpp 复制代码
// 使用GC重建保护器,防止在构造过程中被垃圾回收
FGCReconstructionGuard GCGuard;
bool bRecycledSubobject = false;    
Result = StaticAllocateObject(InClass, InOuter, InName, InFlags, 
                              Params.InternalSetFlags, bCanRecycleSubobjects, 
                              &bRecycledSubobject, ...);

GCGuard的作用机制:

cpp 复制代码
class FGCReconstructionGuard {
    bool bLocked;
public:
    FGCReconstructionGuard() : bLocked(false) {
        if(IsGarbageCollecting()) {
            Lock();
        }
    }
    void Lock() {
        GUObjectArray.LockDisregardForGC();
        bLocked = true;
    }
    void Unlock() {
        if(bLocked) {
            GUObjectArray.UnlockDisregardForGC();
            bLocked = false;
        }
    }
};

1.4 构造函数调用策略

cpp 复制代码
// 只有非复用的子对象才调用构造函数
if (!bRecycledSubobject) {        
    STAT(FScopeCycleCounterUObject ConstructorScope(...));
    (*InClass->ClassConstructor)(FObjectInitializer(Result, Params));
}

性能优化:

  • 复用的子对象跳过构造函数,提升性能
  • 使用统计计数器跟踪构造时间

1.5 事务系统集成

cpp 复制代码
if (GIsEditor && 
    !Result->HasAnyInternalFlags(EInternalObjectFlags::Async | EInternalObjectFlags_AsyncLoading) &&
    GUndo &&
    (InFlags & RF_Transactional) && !(InFlags & RF_NeedLoad) && 
    !InClass->IsChildOf(UField::StaticClass()))
{
    // 三阶段事务处理
    Result->MarkAsGarbage();      // 1. 临时标记为垃圾
    SaveToTransactionBuffer(Result, false); // 2. 保存到撤销缓冲区
    Result->ClearGarbage();       // 3. 清除垃圾标记
}

事务处理原理:

  1. 将新对象标记为垃圾,防止被其他操作引用
  2. 保存对象状态到事务缓冲区,支持撤销操作
  3. 清除垃圾标记,恢复正常使用

2. StaticAllocateObject - 对象分配引擎

核心职责

负责对象内存管理、索引分配和对象替换逻辑。

内存分配流程分析

2.1 名称处理系统

cpp 复制代码
if(InName == NAME_None) {
#if WITH_EDITOR
    if (GOutputCookingWarnings && GetTransientPackage() != InOuter->GetOutermost()) {
        InName = MakeUniqueObjectName(InOuter, InClass, NAME_UniqueObjectNameForCooking);
    } else
#endif
    {
        InName = MakeUniqueObjectName(InOuter, InClass);
    }
} else {
    // 检查名称冲突
    Obj = StaticFindObjectFastInternal(NULL, InOuter, InName, EFindObjectFlags::ExactClass);
}

唯一名称生成算法:

cpp 复制代码
// MakeUniqueObjectName内部实现简析
FName MakeUniqueObjectName(UObject* Outer, UClass* Class, FName BaseName) {
    // 基础算法:在名称后添加递增数字
    // 例如:Component -> Component_0, Component_1, ...
    // 搜索现有对象,找到第一个未使用的数字后缀
}

2.2 对象替换决策树

cpp 复制代码
if(Obj == nullptr) {    
    // 路径A:全新分配
    Obj = (UObject*)GUObjectAllocator.AllocateUObject(TotalSize, Alignment, GIsInitialLoad);
} else {
    // 路径B:替换现有对象
    
    // 保存旧对象状态
    ObjectRestoreAfterInitProps = Obj->GetRestoreForUObjectOverwrite();
    Linker = Obj->GetLinker();
    LinkerIndex = Obj->GetLinkerIndex();
    
    // 判断是否可复用子对象
    if (!bCreatingCDO && (!bCanRecycleSubobjects || !Obj->IsDefaultSubobject()
        #if UE_WITH_REMOTE_OBJECT_HANDLE
        || UE::RemoteObject::Handle::IsRemote(Obj)
        #endif
        )) {
        // 需要销毁重建
        OldIndex = GUObjectArray.ObjectToIndex(Obj);
        OldSerialNumber = GUObjectArray.GetSerialNumber(OldIndex);
        
        // 执行异步销毁
        Obj->ConditionalBeginDestroy();
        while (!Obj->IsReadyForFinishDestroy()) {
            FPlatformProcess::Sleep(0); // 忙等待
        }
        Obj->ConditionalFinishDestroy();
        
        // 释放对象索引
        GUObjectArray.FreeUObjectIndex(Obj);
        Obj->~UObject();
        bWasConstructedOnOldObject = true;
    } else {
        // 直接复用子对象
        bSubObject = true;
    }
}

异步销毁机制:

cpp 复制代码
void UObject::ConditionalBeginDestroy() {
    // 标记开始销毁
    SetFlags(RF_BeginDestroyed);
    // 调用BeginDestroy,允许资源异步释放
    BeginDestroy();
}

bool UObject::IsReadyForFinishDestroy() {
    // 检查所有异步操作是否完成
    return !HasAnyFlags(RF_AsyncLoadingFlags);
}

void UObject::ConditionalFinishDestroy() {
    // 执行最终清理
    FinishDestroy();
    SetFlags(RF_FinishDestroyed);
}

2.3 UObjectBase构造策略

cpp 复制代码
if (!bSubObject) {
    // 全新构造或替换构造
    UE_AUTORTFM_OPEN_NO_VALIDATION {
        FMemory::Memzero((void*)Obj, TotalSize);
        new ((void*)Obj) UObjectBase(
            const_cast<UClass*>(InClass), 
            InFlags|RF_NeedInitialization, 
            InternalSetFlags, 
            InOuter, 
            InName, 
            OldIndex, 
            OldSerialNumber == 0 ? SerialNumber : OldSerialNumber, 
#if UE_WITH_REMOTE_OBJECT_HANDLE
            OldRemoteId.IsValid() ? OldRemoteId : RemoteId
#endif
        );
    };
    
    // AutoRTFM事务支持
    UE_AUTORTFM_ONABORT(Obj) {
        Obj->SetInternalFlags(EInternalObjectFlags::AutoRTFMConstructionAborted);
    };
} else {
    // 复用子对象,只更新标志
    Obj->SetFlags(InFlags);
    Obj->SetInternalFlags(InternalSetFlags);
}

UObjectBase构造函数关键初始化:

cpp 复制代码
UObjectBase::UObjectBase(UClass* InClass, EObjectFlags InFlags, ...) {
    // 1. 设置类引用
    this->ClassPrivate = InClass;
    
    // 2. 设置标志位
    this->ObjectFlags = InFlags;
    
    // 3. 设置外部包
    this->OuterPrivate = InOuter;
    
    // 4. 注册到全局对象数组
    GUObjectArray.AllocateUObjectIndex(this);
    
    // 5. 哈希表注册(用于快速查找)
    Register(this);
}

2.4 链接器系统集成

cpp 复制代码
if (bWasConstructedOnOldObject) {
    // 重新关联链接器
    Obj->SetLinker(Linker, LinkerIndex, false);
    if(Linker) {
        check(Linker->ExportMap[LinkerIndex].Object == NULL);
        Linker->ExportMap[LinkerIndex].Object = Obj;
    }
}

链接器系统作用:

  • 管理从磁盘加载的对象
  • 维护导出表索引映射
  • 支持对象懒加载

3. FUObjectAllocator::AllocateUObject - 底层内存分配器

核心职责

提供两种内存分配策略:普通堆分配和持久化分配。

分配策略实现

cpp 复制代码
UObjectBase* FUObjectAllocator::AllocateUObject(int32 Size, int32 Alignment, bool bAllowPermanent) {
    void* Result = nullptr;

    // AutoRTFM事务支持:非事务化分配
    UE_AUTORTFM_ALWAYS_OPEN
    
    if (bAllowPermanent && !GPersistentAllocatorIsDisabled) {
        // 策略A:持久化分配器
        Result = GetPersistentLinearAllocator().Allocate(Size, Alignment);
    } else {
        // 策略B:普通堆分配
        Result = FMemory::Malloc(Size, Alignment);
    }

    return (UObjectBase*)Result;
}

两种分配器对比

持久化线性分配器 (PersistentLinearAllocator)

适用场景:

  • 类默认对象(CDO)
  • 启动时加载的核心对象
  • 永不销毁的永久对象

技术特点:

cpp 复制代码
class FPersistentLinearAllocator {
    void* Current;      // 当前分配位置
    void* End;          // 分配器结束位置
    int64 TotalSize;    // 总分配大小
    
    void* Allocate(int32 Size, int32 Alignment) {
        // 线性分配算法
        void* AlignedPtr = Align(Current, Alignment);
        if ((char*)AlignedPtr + Size <= (char*)End) {
            Current = (char*)AlignedPtr + Size;
            return AlignedPtr;
        }
        // 空间不足,回退到FMemory::Malloc
        return FMemory::Malloc(Size, Alignment);
    }
};

优点:

  1. 零碎片化:线性分配无内存碎片
  2. 高性能:指针移动即可,无复杂算法
  3. 缓存友好:连续分配,提高缓存命中率

缺点:

  1. 无法释放 :分配的内存永久占用

  2. 大小固定:需要预分配足够空间

普通堆分配器 (FMemory::Malloc)

实现机制

cpp 复制代码
// UE4/5内存分配器架构
class FMalloc* GMalloc = nullptr;

void* FMemory::Malloc(SIZE_T Count, uint32 Alignment) {
    return GMalloc->Malloc(Count, Alignment);
}

// 具体实现可能是:
// - FMallocBinned(分箱分配器)
// - FMallocAnsi(系统malloc包装)
// - FMallocStomp(调试分配器)
// - FMallocThreadSafe(线程安全分配器)

分配策略选择:

cpp 复制代码
// GIsInitialLoad的判断逻辑
bool GIsInitialLoad = false;

void UObjectBase::InitUObject() {
    if (GIsInitialLoad) {
        // 启动阶段,使用持久化分配器
        bAllowPermanent = true;
    } else {
        // 运行时,使用普通分配器
        bAllowPermanent = false;
    }
}

系统协同工作流程

典型用例:创建Actor实例

cpp 复制代码
// 用户代码
AActor* NewActor = GetWorld()->SpawnActor<AActor>(ActorClass);

// 内部调用链
1. UWorld::SpawnActor
   ↓
2. AActor::StaticClass()->ClassDefaultObject
   ↓
3. StaticConstructObject_Internal(
        FStaticConstructObjectParameters(
            Class: AActor::StaticClass(),
            Outer: Level,
            Name: NAME_None,
            Template: CDO
        ))
   ↓
4. StaticAllocateObject(
        InClass: AActor,
        InOuter: Level,
        InName: GeneratedName,  // 如"Actor_42"
        bCanRecycleSubobjects: false)
   ↓
5. FUObjectAllocator::AllocateUObject(
        Size: AActor::GetPropertiesSize(),  // 约1-4KB
        Alignment: 8,
        bAllowPermanent: false)  // 运行时创建,不使用持久化
   ↓
6. 内存分配完成,返回指针
   ↑
7. StaticAllocateObject构造UObjectBase,注册到GUObjectArray
   ↑
8. StaticConstructObject_Internal调用AActor构造函数
   ↑
9. UWorld将Actor添加到世界场景

性能优化技术

1. 对象池技术
cpp 复制代码
// 子对象复用减少分配开销
if (bCanRecycleSubobjects) {
    // 直接复用现有对象,节省:
    // - 内存分配时间
    // - 构造函数执行时间
    // - 哈希表注册时间
}
2. 延迟初始化
cpp 复制代码
// RF_NeedInitialization标志
Obj->SetFlags(InFlags | RF_NeedInitialization);
// 稍后在合适时机完成初始化
3. 异步加载集成
cpp 复制代码
// 异步加载线程中的特殊处理
if (IsInAsyncLoadingThread()) {
    ThreadContext.AsyncPackageLoader->NotifyConstructedDuringAsyncLoading(Obj, bSubObject);
    // 避免阻塞主线程,异步完成对象设置
}

错误处理和健壮性

内存分配失败处理
cpp 复制代码
void* FUObjectAllocator::AllocateUObject(...) {
    void* Result = ...;
    
    // 检查分配是否成功
    if (UNLIKELY(Result == nullptr)) {
        // 触发内存不足处理
        FPlatformMemory::OnOutOfMemory(Size, Alignment);
    }
    
    return Result;
}
对象泄漏检测
cpp 复制代码
// 在StaticAllocateObject中
if (Obj && !Obj->IsDefaultSubobject()) {
    // 检查对象是否已正确注册
    check(GUObjectArray.ObjectToIndex(Obj) != INDEX_NONE);
    
    // 检查对象是否被正确标记
    if (bCreatingCDO) {
        check(Obj->HasAllFlags(RF_ClassDefaultObject));
    }
}

线程安全机制

GC保护锁
cpp 复制代码
// FGCReconstructionGuard的多线程保护
void StaticAllocateObject(...) {
    if (ensureMsgf(IsInGameThread(), ...) && GCGuard) {
        GCGuard->Lock();  // 锁定GC,防止并发问题
    }
    
    // ... 执行对象操作
    
    if (GCGuard && GCGuard->IsLocked()) {
        GCGuard->Unlock();  // 完成后解锁
    }
}
异步标志清理
cpp 复制代码
// 确保对象从异步状态正确转换
if (IsInGameThread() && Obj->HasAnyInternalFlags(EInternalObjectFlags::Async)) {
    Obj->ClearInternalFlags(EInternalObjectFlags::Async);
}

总结

设计模式应用

  1. 工厂模式:StaticConstructObject_Internal作为对象工厂
  2. 建造者模式:FObjectInitializer作为参数建造器
  3. 策略模式:多种内存分配策略
  4. 观察者模式:对象创建事件广播

性能关键点

  1. 内存分配策略选择:根据场景选择合适的分配器
  2. 对象复用机制:原生类子对象的智能复用
  3. 批量操作优化:GC保护锁减少锁竞争
  4. 异步处理:支持异步加载和构造
相关推荐
速冻鱼Kiel6 小时前
Lyra的相机系统
笔记·ue5·游戏引擎·虚幻
暮志未晚Webgl7 小时前
UE5模型面数优化
ue5
zhangzhangkeji7 小时前
UE5 C++(45):射线检测多物体的的按通道与按对象类型 LineTraceMultiByObjectType
ue5
成都渲染101云渲染666613 小时前
5090 显卡云端上线!Blender / Maya / UE5 渲染速度再提升,云渲染成主流选择
ue5·blender·maya
一个响当当的名号1 天前
lectrue5 存储模型和压缩
ue5
zhangzhangkeji2 天前
UE5 C++(44-4):对比一下蓝图中的射线检测节点,源代码,按通道与按对象类型
ue5
暮志未晚Webgl2 天前
UE使用内置功能查看性能
ue5
AI视觉网奇2 天前
Epic linux 打包。
笔记·学习·ue5
伪善者2 天前
UE5 打包插件
ue5·打包