UE4.26垃圾回收机制初探(一)

1.探究初衷

  最近在研究UE4引擎,不可避免的遇到垃圾回收机制的学习,在网上看了一堆的介绍之后,依旧云里雾里。主要的疑惑有以下几个:

  1. 都说UObject会自动回收,可是我们创建的UOBject是如何被引擎添加到管理队列里的;
  2. UObject是在何时进行回收的;
  3. AddToRoot为何能保证UObject不被自动回收;
  4. MarkPendingKill函数又干了什么可以让UObject销毁。

2.探究历程

  我刚开始猜想应该是在NewObject的过程中进行了某些操作,所以进去看了下,为了方便阅读,直接把代码贴在这里:

// Epic Games\UE_4.26\Engine\Source\Runtime\CoreUObject\Public\UObject\UObjectGlobals.h  Line 1123
/**
 * Convenience template for constructing a gameplay object
 *
 * @param	Outer		the outer for the new object.  If not specified, object will be created in the transient package.
 * @param	Class		the class of object to construct
 * @param	Name		the name for the new object.  If not specified, the object will be given a transient name via MakeUniqueObjectName
 * @param	Flags		the object flags to apply to the new object
 * @param	Template	the object to use for initializing the new object.  If not specified, the class's default object will be used
 * @param	bCopyTransientsFromClassDefaults	if true, copy transient from the class defaults instead of the pass in archetype ptr (often these are the same)
 * @param	InInstanceGraph						contains the mappings of instanced objects and components to their templates
 * @param	ExternalPackage						Assign an external Package to the created object if non-null
 *
 * @return	a pointer of type T to a new object of the specified class
 */
template< class T >
FUNCTION_NON_NULL_RETURN_START
	T* NewObject(UObject* Outer, const UClass* Class, FName Name = NAME_None, EObjectFlags Flags = RF_NoFlags, UObject* Template = nullptr, bool bCopyTransientsFromClassDefaults = false, FObjectInstancingGraph* InInstanceGraph = nullptr, UPackage* ExternalPackage = nullptr)
FUNCTION_NON_NULL_RETURN_END
{
   
	if (Name == NAME_None)
	{
   
		FObjectInitializer::AssertIfInConstructor(Outer, TEXT("NewObject with empty name can't be used to create default subobjects (inside of UObject derived class constructor) as it produces inconsistent object names. Use ObjectInitializer.CreateDefaultSubobject<> instead."));
	}

#if DO_CHECK
	// Class was specified explicitly, so needs to be validated
	CheckIsClassChildOf_Internal(T::StaticClass(), Class);
#endif

	FStaticConstructObjectParameters Params(Class);
	Params.Outer = Outer;
	Params.Name = Name;
	Params.SetFlags = Flags;
	Params.Template = Template;
	Params.bCopyTransientsFromClassDefaults = bCopyTransientsFromClassDefaults;
	Params.InstanceGraph = InInstanceGraph;
	Params.ExternalPackage = ExternalPackage;
	return static_cast<T*>(StaticConstructObject_Internal(Params));
}

  我顺腾摸瓜查看了StaticConstructObject_Internal相关函数,发现里面只是一些创建代码,并没有关于对象垃圾回收相关的代码。巧合的是我在***StaticConstructObject_Internal(const FStaticConstructObjectParameters& Params)***函数里看到了MarkPendingKill函数,点进去看了一下,线索来了。看一下MarkPendingKill函数的代码:

// Epic Games\UE_4.26\Engine\Source\Runtime\CoreUObject\Public\UObject\UObjectBaseUtility.h Line171
/**
 * Marks this object as RF_PendingKill.
 */
FORCEINLINE void MarkPendingKill()
{
   
	check(!IsRooted());
	GUObjectArray.IndexToObject(InternalIndex)->SetPendingKill();
}

  我发现了GUObjectArray,很显然这是一个管理UOBject的数组,他很有可能就是我在开头提到的管理队列。我们看下他的声明

// Epic Games\UE_4.26\Engine\Source\Runtime\CoreUObject\Private\UObject\UObjectHash.cpp Line 24
// Global UObject array instance
FUObjectArray GUObjectArray;

  接下来再看下FUObjectArray的声明,篇幅原因这里只贴部分代码:

// D:\Program Files\Epic Games\UE_4.26\Engine\Source\Runtime\CoreUObject\Public\UObject\UObjectArray.h Line 573
/***
*
* FUObjectArray replaces the functionality of GObjObjects and UObject::Index
*
* Note the layout of this data structure is mostly to emulate the old behavior and minimize code rework during code restructure.
* Better data structures could be used in the future, for example maybe all that is needed is a TSet<UObject *>
* One has to be a little careful with this, especially with the GC optimization. I have seen spots that assume
* that non-GC objects come before GC ones during iteration.
*
**/
class COREUOBJECT_API FUObjectArray
{
   
public:
	/**
	 * Constructor, initializes to no permanent object pool
	 */
	FUObjectArray();
	
	/**
	 * Adds a uobject to the global array which is used for uobject iteration
	 *
	 * @param	Object Object to allocate an index for
	 */
	void AllocateUObjectIndex(class UObjectBase* Object, bool bMergingThreads = false);
}

  我单独把AllocateUObjectIndex这个成员函数贴出来了,显然只要找到这个函数在哪里被引用的,自然可以解决开头提出的第一个问题。查找引用后得出引用的地方:

// Epic Games\UE_4.26\Engine\Source\Runtime\CoreUObject\Private\UObject\UObjectHash.cpp Line 1253
void AllocateUObjectIndexForCurrentThread(UObjectBase* Object)
{
   
	GUObjectArray.AllocateUObjectIndex(Object);
}

  OK,接近了,再查找一下这个函数的被引用情况:

// Epic Games\UE_4.26\Engine\Source\Runtime\CoreUObject\Private\UObject\UObjectBase.cpp Line 165
/**
 * 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;
	}
	AllocateUObjectIndexForCurrentThread(this);//看这里看这里看这里看这里看这里看这里
	check(InName != NAME_None && InternalIndex >= 0);
	if (InternalFlagsToSet != EInternalObjectFlags::None)
	{
   
		GUObjectArray.IndexToObject(InternalIndex)->SetFlags(InternalFlagsToSet);
	
	}	
	HashObject(this);
	check(IsValidLowLevel()<
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

鬼马行天

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值