文章目录
- [1. StaticConstructObject_Internal - 顶层构造管理器](#1. StaticConstructObject_Internal - 顶层构造管理器)
- [2. StaticAllocateObject - 对象分配引擎](#2. StaticAllocateObject - 对象分配引擎)
- [3. FUObjectAllocator::AllocateUObject - 底层内存分配器](#3. FUObjectAllocator::AllocateUObject - 底层内存分配器)
- 总结
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);
复用条件解析:
- 必须为原生类(CLASS_Native或CLASS_Intrinsic)
- 必须从CDO派生或使用正确的原型
- 不能是构造函数中的默认子对象(除非特殊标记)
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. 清除垃圾标记
}
事务处理原理:
- 将新对象标记为垃圾,防止被其他操作引用
- 保存对象状态到事务缓冲区,支持撤销操作
- 清除垃圾标记,恢复正常使用
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);
}
};
优点:
- 零碎片化:线性分配无内存碎片
- 高性能:指针移动即可,无复杂算法
- 缓存友好:连续分配,提高缓存命中率
缺点:
-
无法释放 :分配的内存永久占用
-
大小固定:需要预分配足够空间
普通堆分配器 (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);
}
总结
设计模式应用
- 工厂模式:StaticConstructObject_Internal作为对象工厂
- 建造者模式:FObjectInitializer作为参数建造器
- 策略模式:多种内存分配策略
- 观察者模式:对象创建事件广播
性能关键点
- 内存分配策略选择:根据场景选择合适的分配器
- 对象复用机制:原生类子对象的智能复用
- 批量操作优化:GC保护锁减少锁竞争
- 异步处理:支持异步加载和构造