(UE4 4.26)UE4 UObject的内存管理机制浅析

前言

在上一章中 UE4 C++ FMemory内存管理从入门到深入  介绍了FMemory的内存管理方式, 这节介绍了UObject的内存管理,UObject的内存管理是建立在FMemory这套的底层内存分配机制上的。本文重点介绍UObject的UPROPERTY标记内存回收,至于UObject的内存分配等等其他大致略过。

UObject的内存分配

UObject的内存分配大致流程

 具体流程如上所示, UObject的内存分配器叫GUObjectAllocator,其内存分配是在FMemory的基础上进行一个封装而已, 这里不介绍太多。至于UObject被分配内存后,每一个UObject对象被一个FUObjectItem对象包裹放到GUObjectArray数组中,进行全局的管理。FUObjectItem和GUObjectArray下面在详细说。

UObject 内存垃圾回收(GC)总体执行流程

这里主要讲解UPROPERTY标记法的UObject变量内存回收机制。

GC函数调用栈

大体总结为:

分析UPROPERTY, 生成标记引用信息

UClass注册时分析所有UPROPERTY标注的变量, 生成类的FGCReferenceTokenStream信息

  

FGCReferenceTokenStream类内部变量是个TArray<uint32>,包含了UObjectUPROPERTY变量的引用信息(FGCReferenceInfo), 对于GC Mark阶段,用于分析被UPROPERTY标记的UObject在引用链是否有效

GC条件触发

UE4触发GC的执行入口函数的 UEngine::PerformGarbageCollectionAndCleanupActors()

而是否触发GC的判断是每帧都在进行的,一旦满足条件就进行GC

UE4每隔一段时间GC一次,这个“一段时间”可以在引擎里进行设置 

GC执行

从上面的图中已经看到GC主要分为两个阶段:

GC Mask(GC标记Object状态阶段)和UObject Memory Collect内存清除

GC对象和GC状态

先看看UObject GC 状态有关的主要数据结构

可以看到每一个UObject对象都被一个FUObjectItem管理着,  FChunkedFixedUObjectArray管理着一个个内存块,一个内存块包含N个FUObjectItem对象,你可以简单的这么认为,GUObjectArray是管理FUObjectItem(UObject)对象的一个大型数组。 FUObjectItem对象有个int32 Flags变量专门用于指明UObject对象此时的GC状态

 举个例子,我们调用 AActor::Destroy 销毁一个Actor对象

 

AActor执行Destroy的时候用 将自己所在的FUObjectItem标记状态为EInternalObjectFlags::PendingKill

这个时候UObject对象是处于随时可能被内存回收的,因此判断一个UObject对象是否是安全的,不仅仅判断是否为空,也要判断其是否处于 PendingKill状态

if(Object != nullptr && !Object->IsPendingKill()

 至于前面的文章中说过调用UObject::AddToRoot()就不会被UE4进行内存回收, 是因为此时FUObjectItem标记状态为EInternalObjectFlags::RootSet这种状态下UObject是不会被内存回收的。具体的原因下面再说。

GC标记过程

GC标记过程主要发生在GarbageCollection.cpp::CollectGarbageInternal, 此时会对所有的UObject对象先进行可达性分析,可达性分析主要是分析一个UObject对象是否可达的,如果不可达就标记为EInternalObjectFlags::Unreachable,才被认为是无效内存进一步参与下面的GC步骤被回收内存。主要分为两部分(蓝色部分)

1.先把没有KeepFlags 和EInternalObjectFlags::GarbageCollectionKeepFlags标志的所有UObject对象标记为EInternalObjectFlags::Unreachable,这个过程是多线程的。

MarkObjectsAsUnreachable并行判断可达性的时候看到这样一句代码,对于处于EInternalObjectFlags::RootSet的对象并不会被标记为不可达状态,这解释了上面UObject::AddToRoot可以让UObject对象不会被GC的说法。

2. 对UPROPERTY标记的Object类对象进行可达性分析

这个分析过程ProcessObjectArray是多线程进行的

在分析可达性中,用到了UClass的变量引用信息FGCReferenceTokenStream。 针对UPROPERTY标记UObject变量的各种情况(单个UObject对象,TArray<UObject>, 包含UObjectTMap等等)进行不同的归类进行对象的可达性分析,如果引用链上UObject被判定不可达,也会被标记为EInternalObjectFlags::Unreachable

上面两部分可达性分析,将认定无效的UObject对象进行了EInternalObjectFlags::Unreachable标记

收集不可达的UObject对象

 GatherUnreachableObjects收集不可达的FUObjectItem对象,放在一个全局不可达对象数组

 开始清理Object阶段

UnhashUnreachableObjects,对不可达的UObject进行进一步清理,清除UObject部分信息,如linker's export table等等,最后标记为RF_BeginDestroyed(注意这里是Object的状态而非FUObjectItem状态的标记)

 

 

完成UObject信息清理阶段

UObject已经在上一步进行初步信息清理, 这里得进行所有信息的清理,最后标记为RF_FinishDestroyed 

 

 内存回收阶段

上面阶段的UObject最终被清除完各种信息, 被标记为RF_FinishDestroyed,之后并对UObject进行真正的内存回收。UObject的内存回收在FAsyncPurge中进行

可以看到FAsyncPurge继承FRunnable,是UE4多线程情景下使用的一个类, 但是在内存回收时

UE4可能存在单线程(同步)或者多线程(异步)内存回收两种情况:

 资料参考

【1】浅析UE4垃圾回收

  • 4
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值