[toc] UObject 对象无法被手动释放,只能被手动请求 ConditionalBeginDestroy 来完成销毁。实际上,这个操作只是设置了当前 UObjectRF_BeginDestroyed 为真,然后通过 SetLinker 函数将当前对象从 linker 导出表中清除。实际的销毁操作,则是在 GC 流程中进行的。 通常的 GC 主要分为以下几个部分:
- GC 对象容器 - GC 触发入口 (GabageCollection->CollectGarbage) - GC 流程

# GC 对象容器入口

1
UCookCommandlet::ConditionalCollectGarbage()

在这个入口里有这样一段代码

1
int32 NumObjectsBeforeGC = GUObjectArray.GetObjectArrayNumMinusAvailable();

GUObjectArray 保存在 UObjectHash 的全局作用域中

1
2
// Global UObject array instance
FUObjectArray GUObjectArray;

所有存活的 Object 会存放到 FUObjectArrayObjObjects 容器中

1
2
/** Array of all live objects.                                          */
TUObjectArray ObjObjects;

另外,UObject 会被 Wrap 一层成 FUObjectItem 来存放到 FUObjectArray

1
2
3
4
5
6
7
void FUObjectArray::AllocateUObjectIndex(UObjectBase* Object, bool bMergingThreads /*= false*/)
{
...
// Add to global table.
FUObjectItem* ObjectItem = IndexToObject(Index);
...
}

# FUObjectItem

  1. 用来存储 UObject 的引用
  2. 默认会先分配 MaxChunks 个 FUObjectItem
    UObjectBaseInit()->GUObjectArray.AllocateObjectPool()->ObjObjects.PreAllocate()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void PreAllocate(int32 InMaxElements, bool bPreAllocateChunks) TSAN_SAFE
{
//计算需要的块数
MaxChunks = InMaxElements / NumElementsPerChunk + 1;
//计算最大元素数
MaxElements = MaxChunks * NumElementsPerChunk;
//Objects用于存储每块首个ObjectItem的指针
Objects = new FUObjectItem*[MaxChunks];
FMemory::Memzero(Objects, sizeof(FUObjectItem*) * MaxChunks);
if (bPreAllocateChunks)
{
//MaxElements个连续ObjectItem的内存块
PreAllocatedObjects = new FUObjectItem[MaxElements];
for (int32 ChunkIndex = 0; ChunkIndex < MaxChunks; ++ChunkIndex)
{
//记录每块的首个指针地址
Objects[ChunkIndex] = PreAllocatedObjects + ChunkIndex * NumElementsPerChunk;
}
NumChunks = MaxChunks;
}
}

由上述代码可以看出,实际上所有 ChunkFUObjectItem 都是存放在一个顺序表中的 ( PreAllocatedObjects ), 而每个 Chunk 会存放对应 Chunk 的首地址,并且所有 Chunk 的元素数量是一致的

当然,如果发现 Chunk 数量不足时,会继续向后申请新的 Chunk

大概概括一下 UObjectArray 的作用: 1. 全局存储对象的作用 2. 全局对象列表管理的是 ObjectItem,而不是 Object,起到分离和记录额外信息的作用(如 GC 时,Object 并不存储 GC 信息,Object 设计时也就不用考虑 GC 问题) 3. 实现忽略 GC 功能(算一个作用吧)

# UObject 在哪里将自身引用到全局数组中?

  1. UOject 继承自 UObjectBase
  2. 构造函数调用 AddObject
  3. AddObject 注册引用
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
/**
* Add a newly created object to the name hash tables and the object array
*
* @param Name name to assign to this uobject
*/
void UObjectBase::AddObject(FName InName, EInternalObjectFlags InSetInternalFlags)
{
NamePrivate = InName;
EInternalObjectFlags InternalFlagsToSet = InSetInternalFlags;
if (!IsInGameThread())
{
InternalFlagsToSet = EInternalObjectFlags::Async;
}
if (ObjectFlags & RF_MarkAsRootSet)
{
InternalFlagsToSet = EInternalObjectFlags::RootSet;
ObjectFlags &= ~RF_MarkAsRootSet;
}
if (ObjectFlags & RF_MarkAsNative)
{
InternalFlagsToSet = EInternalObjectFlags::Native;
ObjectFlags &= ~RF_MarkAsNative;
}
GUObjectArray.AllocateUObjectIndex(this);
check(InName != NAME_None && InternalIndex >= 0);
if (InternalFlagsToSet != EInternalObjectFlags::None)
{
GUObjectArray.IndexToObject(InternalIndex)->SetFlags(InternalFlagsToSet);

}
HashObject(this);
check(IsValidLowLevel());
}
更新于

请我喝[茶]~( ̄▽ ̄)~*

Solvarg 微信支付

微信支付

Solvarg 支付宝

支付宝

Solvarg 贝宝

贝宝