337p人体粉嫩胞高清图片,97人妻精品一区二区三区在线 ,日本少妇自慰免费完整版,99精品国产福久久久久久,久久精品国产亚洲av热一区,国产aaaaaa一级毛片,国产99久久九九精品无码,久久精品国产亚洲AV成人公司
網(wǎng)易首頁 > 網(wǎng)易號 > 正文 申請入駐

UE5版本中GC的改動

0
分享至


【USparkle專欄】如果你深懷絕技,愛“搞點(diǎn)研究”,樂于分享也博采眾長,我們期待你的加入,讓智慧的火花碰撞交織,讓知識的傳遞生生不息!

這是侑虎科技第1958篇文章,感謝作者南京周潤發(fā)供稿。歡迎轉(zhuǎn)發(fā)分享,未經(jīng)作者授權(quán)請勿轉(zhuǎn)載。如果您有任何獨(dú)到的見解或者發(fā)現(xiàn)也歡迎聯(lián)系我們,一起探討。(QQ群:793972859)

作者主頁:

https://www.zhihu.com/people/xu-chen-71-65

總覽

UE5的GC,相對UE4做了一些改動,但本質(zhì)沒變,依然是基于UObject的標(biāo)記清掃算法,許多改動都屬于優(yōu)化。

先看GC的各個(gè)階段:

  • 遍歷所有Object,標(biāo)記為不可達(dá)

改動較少,增加了RefCount引用方式;更改了Unreachable Flag;增加了單獨(dú)的RootArray作為根集。

  • 遍歷引用關(guān)系做可達(dá)性分析

改動較多,Batch模式的引用收集;UObject對ClassPrivate和Outer的引用從Token中移除;增加了引用收集的Debug模式。

  • 清理不可達(dá)Object

差別不大,依然是分幀Purge,對每個(gè)UObject依次執(zhí)行BeginDestroy, Destroy, FinishDestroy。

除此之外,還有實(shí)驗(yàn)性的Increment可達(dá)性分析模式,想完全避免可達(dá)性分析造成的一幀卡頓,算是一個(gè)最大的不同。

對于GC Cluster,變化不大。

另外還有一些其他Tips,比如IsPendingKill接口改成了IsGarbage;ObjectFlags增加了Reachable0,Reachable1,Reachable2等等。

文章中所使用的為UE5.7。

前篇:

一、加入引用計(jì)數(shù)機(jī)制

1. RefCounted Flag & RefCount

觀察InternalObjectFlags,發(fā)現(xiàn)多了一個(gè)叫RefCounted的Flag,說明UE的GC在標(biāo)記清掃的基礎(chǔ)上,又加入了引用計(jì)數(shù)機(jī)制,算是一個(gè)不小的改動。

引用計(jì)數(shù)是另一種GC方式,Python中就使用了,簡單概括為每個(gè)Object記錄自己被多少個(gè)其他Object引用了,當(dāng)發(fā)現(xiàn)引用計(jì)數(shù)為0時(shí),就判定自己為垃圾。至于垃圾的清理,可以選擇為0時(shí)立即清理,也可以選擇之后一起清理,反正垃圾是不會再被重新引用到的。

enum class EInternalObjectFlags : int32
{
None = 0,
//...
RefCounted UE_DEPRECATED(5.7, "Use GetRefCount() to determine if a refcount exists instead.") = 1 << 29, ///< Object currently has ref-counts associated with it.
//...
};

但有了Flag還不夠,得給每個(gè)Object再定義一個(gè)RefCount屬性,記錄被引用的次數(shù)。UE5.7之前,在FUObjectItem中有一個(gè)專門的int32 RefCount屬性,但UE5.7開始把RefCount和Flags一起打包成了int64。Flag存在高32位里,RefCount存在低32位里。



這樣有個(gè)好處,RefCount通常用不了int32這么大,因此可以把更多位數(shù)給EInternalObjectFlags用,比如添加一些我們自定義的Flag,或者UE引擎自己擴(kuò)展。


2. 操作接口

添加RefCount:

void UObjectBase::AddRef() const
{
FUObjectItem* ObjectItem = GUObjectArray.ObjectToObjectItem(this);
ObjectItem->AddRef();
}

減去RefCount:

void UObjectBase::ReleaseRef() const
{
FUObjectItem* ObjectItem = GUObjectArray.ObjectToObjectItem(this);
ObjectItem->ReleaseRef();
}

獲取RefCount:

在引擎內(nèi)部代碼,比如GC過程中,才有獲取RefCount的需求,因此UObject上沒有獲取RefCount的接口,只有FUObjectItem上有。

FORCEINLINE int32 GetRefCount() const
{
return RefCount;
}

哪些地方會給UObject加上引用計(jì)數(shù)?

引用計(jì)數(shù)管理UObject顯然更容易出錯(cuò),萬一AddRef/ReleaseRef不成對,就會造成UObject泄漏,引擎中目前也只有StrongObjectPtr使用了。而且是用了RAII模式,在構(gòu)造和析構(gòu)中操作RefCount,防止泄漏。StrongObjectPtr可以對一個(gè)UObject添加強(qiáng)引用,防止被GC,和AddToRoot類似。下面是StrongObjectPtr中操作的代碼:

FORCEINLINE_DEBUGGABLE void Reset(ObjectType* InNewObject)
{
if (InNewObject)
{
if (Object == InNewObject)
{
return;
}


if (Object)
{
// UObject type is forward declared, ReleaseRef() is not known.
// So move the implementation to the cpp file instead.
UEStrongObjectPtr_Private::ReleaseUObject(Object);
}
InNewObject->AddRef();
Object = InNewObject;
}
else
{
Reset();
}
}

3. 為什么要使用引用計(jì)數(shù)管理UObject

一個(gè)原因可能是能減少可達(dá)性分析時(shí)間。

可達(dá)性分析階段需要遍歷Object間的所有引用關(guān)系,耗時(shí)正比于引用數(shù)量。

比如下圖,4個(gè)UObject相互引用,總共要遍歷10個(gè)引用關(guān)系。但假如都用引用計(jì)數(shù)管理,可以省略遍歷開銷,直接判定四個(gè)對象都可達(dá)。


但是理論上講,引用計(jì)數(shù)方式只是把遍歷開銷分?jǐn)偟搅诉\(yùn)行時(shí)維護(hù)引用計(jì)數(shù)的加減上。而且注意到上面例子其實(shí)是環(huán)形引用的情況,沒有外部引用情況下,四個(gè)UObject都該被判定為垃圾才對。因此一個(gè)完善的垃圾回收實(shí)現(xiàn),不會只用引用計(jì)數(shù),比如Python的FullGC就用的標(biāo)記-清掃算法,作為輔助。UE依然把標(biāo)記-清掃作為主GC流程,引用計(jì)數(shù)只是小小的輔助,但使用是絕對要注意。

另一個(gè)原因是單獨(dú)針對StrongObjectPtr的優(yōu)化。

StrongObjectPtr之前的實(shí)現(xiàn)是把包起來的UObject單獨(dú)放到一個(gè)大數(shù)組里,然后這個(gè)數(shù)組被一個(gè)FGCObject添加引用。這樣有個(gè)問題,當(dāng)數(shù)組元素很多時(shí),添加需要遍歷全數(shù)組,判斷是否已添加過,而刪除時(shí)同樣需要遍歷全數(shù)組,找到元素并刪除,遍歷的時(shí)間很可觀。


4. RefCount如何影響可達(dá)性分析

其實(shí)RefCount等同于Root,觀察AddRef調(diào)用鏈,最終會執(zhí)行AddToRoot操作。



在第一步標(biāo)記所有UObject不可達(dá)后,會遍歷GRoots容器,把其中的UObject變成可達(dá),那么RefCounted的Object也變成可達(dá)了。

二、可達(dá)性分析階段的優(yōu)化

1. FPrefetchingObjectIterator

首先介紹一個(gè)挺細(xì)節(jié)的ObjectIterator,基于經(jīng)驗(yàn)做了Prefetch,盡量避免訪問Object的Outer和ClassPrivate造成的CacheMiss。整個(gè)可達(dá)性分析階段都充分應(yīng)用了Prefetch技術(shù)。

首先觀察下面代碼,會遍歷一組UObject,然后分別訪問它們的Class和Outer:

FORCEINLINE_DEBUGGABLE void ProcessObjects(DispatcherType& Dispatcher, TConstArrayView 

 CurrentObjects 
)
{
for (FPrefetchingObjectIterator It(CurrentObjects); It.HasMore(); It.Advance())
{
UObject* CurrentObject = It.GetCurrentObject();
UClass* Class = CurrentObject->GetClass();
UObject* Outer = CurrentObject->GetOuter();
//...
}
}

這樣必然會導(dǎo)致讀Object內(nèi)存操作,相比讀CPU Cache肯定是更慢的。粗略估計(jì),訪問內(nèi)存的延遲是60ns,而訪問CPU L1 Cache的延遲是1ns,當(dāng)中相差不少。


既然已經(jīng)知道要訪問這么多Object的ClassPrivate和Outer屬性,那么能不能提前把它們讀取到CPU Cache中,加快訪問速度?這就是FPrefetchingObjectIterator做的事情,使用了操作系統(tǒng)的Prefetch接口,異步的提出一個(gè)預(yù)讀請求,不需要當(dāng)場等待內(nèi)容返回到CPU Cache,CPU可以繼續(xù)執(zhí)行后面指令。

FPrefetchingObjectIterator迭代器在++時(shí),會Prefetch后面第六個(gè)的Outer和ClassPrivate的Schema,以及后面第16個(gè)的Class。這些數(shù)字都是經(jīng)驗(yàn)值,既需要保證訪問到這塊內(nèi)存時(shí),其已經(jīng)被加載到了CPU Cache,又不能Prefetch太多,從而擠占CPU Cache。


示意圖如下:


2. 基于Batch的Object引用收集

Batch

UE4怎么做的:

給定一個(gè)待分析可達(dá)性的Objects數(shù)組,會不斷遍歷其引用,做廣度優(yōu)先搜索,最終遍歷完所有可達(dá)的UObject,完成任務(wù)。其中碰到StructArray結(jié)構(gòu)時(shí),還會形成類似遞歸的情況,處理代碼更復(fù)雜些。整個(gè)過程由一個(gè)大的ProcessObjectArray函數(shù)實(shí)現(xiàn),總共有600+行。

UE5的Batch:

UE5使用了Batch的遍歷方式,首先把ProcessObjectArray邏輯拆成多個(gè)函數(shù),函數(shù)邏輯精簡,最長不超過90行,對CPU指令Cache更友好,然后多個(gè)函數(shù)形成執(zhí)行流;大概500個(gè)Object作為一個(gè)Batch,依次經(jīng)過這些執(zhí)行流,更少的數(shù)據(jù)量對應(yīng)更少的內(nèi)存使用量,讓CPU Cache足夠應(yīng)對;另外每段處理函數(shù)邏輯變簡單后,可以更容易地使用上面提到的Prefetch技術(shù)做Cache預(yù)取,進(jìn)一步提升效率。


Schema

首先,UE5加入了Schema的概念,其實(shí)就是UE4里的TokenStream,記錄Object帶引用的各成員地址偏移,以及成員類型。


ProcessObjectArray

下面是ProcessObjectsArray的主要流程,首先執(zhí)行ProcessObjects,收集引用到Dispatcher,Dispatcher的具體類型為BatchedDispatcher。CurrentObjects處理完后,再嘗試從全部的ObjectsToSerialize數(shù)組中取出BlockSize數(shù)量的UObject,繼續(xù)執(zhí)行ProcessObjects做引用收集。如果ObjectsToSerialize也空了,就執(zhí)行FlushWork,把引用到的UObject都添加到ObjectsToSerialize中繼續(xù)。


這樣就能把標(biāo)記階段分成多個(gè)Pass,每個(gè)Pass處理一點(diǎn)Object,流程示意圖如下:


ProcessObjects

ProcessObjects用于收集CurrentObjects的所有引用,F(xiàn)PrefetchingObjectIterator已在上面提過。注意到這里把Class和Outer的處理單獨(dú)提出來了,UE4還是在正常的Schema里處理它們??赡苁强紤]到Class和Outer是每個(gè)UObject必備的引用,提出來統(tǒng)一處理能減小Schema大小。


VisitMembers函數(shù)是真正的處理Schema里定義的各種引用,包括UPROPERTY引用單個(gè)Object,TArray 數(shù)組引用等等,這里只給出單個(gè)Object引用和StructArray引用兩個(gè)常見例子。


VisitStructArray只會把StructArray緩存到對應(yīng)容器中,后面Flush時(shí)再處理:

FORCEINLINE_DEBUGGABLE void QueueStructArray(FSchemaView Schema, uint8* Data, int32 Num)
{
StructBatcher.PushStructArray(Schema, Data, Num);
}

ImmutableReference & KillableReference

注意到對于單個(gè)UObject的引用,分了ImmutableReference和KillableReference(FMutableReference),其實(shí)只有UObject對Class和Outer的引用屬于Immutable,其他都屬于Killable。這對UE4而言并沒有提出新的內(nèi)容,只是老內(nèi)容包裝成了新概念。

比如有如下代碼,想強(qiáng)制刪除B,但B又是A的屬性,被引用了

A->Prop = B;

B->MarkAsGarbage();

對于Class和Outer引用,非常強(qiáng)力,強(qiáng)到能阻止對B的強(qiáng)刪,讓B繼續(xù)以Garbage的形式存在。

對于普通的KillableReference,無法阻止,同時(shí)A->Prop屬性會變成nullptr。

因此查看它們的初始化方式,F(xiàn)MutableReference是存儲了指向?qū)傩缘闹羔槨?/p>

// Retains address of reference to allow killing it
struct FMutableReference { UObject** Object; };
struct FImmutableReference { UObject* Object; };

FlushWork

依次處理StructArray和其他Object引用,主要工作就是遍歷各種Array。

FORCEINLINE_DEBUGGABLE void FlushWork(DispatcherType& Dispatcher)
{
if constexpr (DispatcherType::bBatching)
{
if (Dispatcher.FlushToStructBlocks())
{
ProcessStructs(Dispatcher);
}


Dispatcher.FlushQueuedReferences();
}
}

最終通過HandleValidReference函數(shù),把UObject標(biāo)記為可達(dá),并插入到ObjectsToSerialize數(shù)組中,以待后續(xù)遍歷。


三、配套Debug功能的完善

1. gc.history協(xié)助找泄露

其中一個(gè)比較實(shí)用的功能是可以記錄下上次GC過程中,每個(gè)對象被誰引用了,方便排查UObject泄露問題。

看個(gè)例子,假如有如下代碼:

FActorSpawnParameters SpawnParameters;
SpawnParameters.Name = TEXT("TestObject123");
APawn* TestPawn = GetWorld()->SpawnActor (SpawnParameters);
USceneComponent* TempObject = NewObject (TestPawn, USceneComponent::StaticClass());
TempObject->RegisterComponent();
{
TStrongObjectPtr StrongPtr(TempObject);
TestPawn->Destroy();
CollectGarbage(GARBAGE_COLLECTION_KEEPFLAGS, true);
TempObject->MarkAsGarbage();
}


GEngine->Exec(GetWorld(), TEXT("OBJ Refs Name=TestObject123"));

在有StrongObjectPtr的情況下,即使MarkAsGarbage,這次GC也是刪不掉TempObject和TestPawn的,在嚴(yán)格的邏輯下,算是UObject泄露。Obj Refs的輸出也印證了:

LogReferenceChain: (Garbage) Pawn /Game/UEDPIE_0_Zoo.Zoo:PersistentLevel.TestObject123 is not currently reachable. Try using GC history to debug transient leaks with 'gc.historysize 1'

輸出中只能看到UObject泄露了,但打印Obj Refs時(shí),引用現(xiàn)場已經(jīng)沒了,不知道為什么泄露。過去,要調(diào)試這個(gè)泄露,得先記下Object地址,再去GC下數(shù)據(jù)斷點(diǎn),再對Flags下斷點(diǎn)等等操作,非常麻煩。

現(xiàn)在UE5加了HistorySize的Debug功能,會對上次GC存儲Snapshot,之后再輸出。

首先通過如下控制臺指令開啟:

gc.ForceEnableGCProcessor
gc.Historysize 1

然后把Exec代碼改成:

FReferenceChainSearch::FindAndPrintStaleReferencesToObject(TestPawn, EPrintStaleReferencesOptions::Log);

接著就能從GC History中獲取到歷史GC引用了,快速定位到泄露原因,非常方便:

LogLoad: Old Pawn /Game/UEDPIE_0_Zoo.Zoo:PersistentLevel.TestObject123 not cleaned up by GC! Garbage object SceneComponent /Game/UEDPIE_0_Zoo.Zoo:PersistentLevel.TestObject123.SceneComponent_0 was previously being referenced by NULL:
(refcounted<1>) (Garbage) SceneComponent /Game/UEDPIE_0_Zoo.Zoo:PersistentLevel.TestObject123.SceneComponent_0
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
^ This reference is preventing the old Pawn from being GC'd ^
-> UObject* UObject::Outer = (Garbage) Pawn /Game/UEDPIE_0_Zoo.Zoo:PersistentLevel.TestObject123

2. 實(shí)現(xiàn)原理

UE5中,新增了用于Debug的TDebugReachabilityProcessor,在可達(dá)性分析階段,當(dāng)?shù)谝淮伪闅v到一個(gè)Object時(shí),把當(dāng)前路徑記錄下來即可。


四、增量式可達(dá)性分析

1. 為什么要做增量式可達(dá)性分析

增量式的垃圾回收,目前還是Experimental階段,個(gè)人感覺有點(diǎn)意義不明。

Documentation | Epic Developer Community

https://dev.epicgames.com/documentation/en-us/unreal-engine/incremental-garbage-collection-in-unreal-engine

一個(gè)典型例子是Lua虛擬機(jī)實(shí)現(xiàn),采用了黑白灰三色標(biāo)記,UE的實(shí)現(xiàn)也比較類似。

開啟方式:

DefaultEngine.ini加入下面配置:

[ConsoleVariables]
gc.AllowIncrementalReachability=1 ; enables Incremental Reachability Analysis
gc.AllowIncrementalGather=1 ; enables Incremental Gather Unreachable Objects
gc.IncrementalReachabilityTimeLimit=0.002 ; sets the soft time limit to 2ms

為什么有必要把可達(dá)性分析階段改成增量式的?

GC耗時(shí)可以分成三個(gè)部分:所有UObject先標(biāo)記不可達(dá),可達(dá)性分析,銷毀垃圾。其中,銷毀垃圾本身是分幀的,不會造成卡頓。前兩個(gè)部分過去不能分幀,而可達(dá)性分析階段是耗時(shí)主要部分,通常超過第一部分十倍,如果游戲希望維持60幀或更高幀率運(yùn)行,這絕對會是一個(gè)阻礙。為了緩解這個(gè)卡頓,UE很早就提出了Cluster優(yōu)化方案,現(xiàn)在又進(jìn)一步提出了增量GC方案。

為什么之前可達(dá)性分析不能分幀執(zhí)行?

考慮這樣的場景,假如可達(dá)性分析要多幀完成,在中間執(zhí)行了A.XX = B語句,使A引用到B,但A已經(jīng)完成了可達(dá)性分析,UE識別不到這個(gè)新加的引用。那么B就有可能被錯(cuò)誤當(dāng)成垃圾回收掉。


可是這是官方文檔的解釋,但個(gè)人感覺不太符合正常使用場景,還沒想通。不妨把B分成下面兩種情況:

1. B在這次GC開始前已創(chuàng)建,如果此時(shí)B沒有其他引用了,那么代碼里又是如何獲取到B的?存裸指針當(dāng)然可以,但本身也是不規(guī)范的做法。

2. B在這次GC開始后才創(chuàng)建,但新創(chuàng)建UObject默認(rèn)是Reachable的,不會被這次GC刪除。這點(diǎn)和Lua是有區(qū)別的,因?yàn)長ua里Object創(chuàng)建后默認(rèn)不可達(dá)。


這個(gè)也許和增量回收還處于Experimental有關(guān),邏輯還不成熟,不妨順著UE思路繼續(xù)往下看實(shí)現(xiàn)。

2. Write Barrier

解決這種問題還是要靠Write Barrier。得益于UE5中把裸指針替換為TObjectPtr,使得A.XX = B這種語句能被捕獲到了。

TObjectPtr會在Operator =函數(shù)里做檢查,如果發(fā)現(xiàn)正在GC,就會立即把B作為可達(dá)對象,加到全局GReachableObjects或GReachableClusters鏈表中。




之后以GReachableObjects為例,在可達(dá)性分析的PerformReachabilityAnalysisPass函數(shù)開頭,會首先把GReachableObjects容器中的Object作為InitialObjects,當(dāng)成初始可達(dá)對象,然后繼續(xù)做可達(dá)性分析遍歷。


這只是TObjectPtr的處理,我們也會定義些自定義邏輯,然后使用AddReferenceObjects函數(shù)添加引用,這就不太好處理了。

3. 如何實(shí)現(xiàn)分幀

回顧之前的ProcessObjectsArray函數(shù),已經(jīng)實(shí)現(xiàn)了Batch形式的Object遍歷,每趟處理500個(gè)Object,那么分幀也是比較方便實(shí)現(xiàn)的。這一段TimeLimit邏輯控制了每幀可以花費(fèi)多少時(shí)間做可達(dá)性分析,超過了就給下一幀繼續(xù)執(zhí)行,會從CollectGarbage入口進(jìn)來。


既然分幀,就要保留當(dāng)前GC進(jìn)度,要知道還有哪些Object需要遍歷。這些信息就存儲在Context.InitialObjects數(shù)組里。因?yàn)榭蛇_(dá)性分析階段是多線程執(zhí)行的,因此每個(gè)線程都有自己的Context。

其實(shí)不僅可達(dá)性分析階段做了增量式處理,后面的收集不可達(dá)對象階段也支持了增量執(zhí)行。只是耗時(shí)本身不高,邏輯也更簡單,進(jìn)度保留在Context中即可。

開啟增量GC后的Trace如下,可以看到實(shí)現(xiàn)了分幀,但還不能做到完美的Timelimit分幀:


五、其他Tips

1. ReachabilityFlag0, ReachabilityFlag1

UE5在標(biāo)記所有Object不可達(dá)時(shí),不再遍歷所有Object并設(shè)置UnReachable標(biāo)記,而是交替使用兩種ReachabilityFlag,同時(shí)依然有UnReachableFlag,用來表示真正的不可達(dá)Object:

ReachabilityFlag0 = 1 << 14, ///< One of the flags used by Garbage Collector to determine UObject's reachability state
ReachabilityFlag1 = 1 << 15, ///< One of the flags used by Garbage Collector to determine UObject's reachability state
ReachabilityFlag2 = 1 << 16, ///< One of the flags used by Garbage Collector to determine UObject's reachability state


Unreachable = 1 << 28, ///< Object is not reachable on the object graph.

交替兩個(gè)ReachabilityFlag代碼如下:

FORCEINLINE static void SwapReachableAndMaybeUnreachable()
{
// It's important to lock the global UObjectArray so that the flag swap doesn't occur while a new object is being created
// as we set the GReachableObjectFlag on all newly created objects
GUObjectArray.LockInternalArray();

Swap(ReachableObjectFlag, MaybeUnreachableObjectFlag);

// Maintain the old flag variables for backwards compatibility
PRAGMA_DISABLE_DEPRECATION_WARNINGS
UE::GC::GReachableObjectFlag = ReachableObjectFlag;
UE::GC::GMaybeUnreachableObjectFlag = MaybeUnreachableObjectFlag;
PRAGMA_ENABLE_DEPRECATION_WARNINGS

GUObjectArray.UnlockInternalArray();
}

如此能實(shí)現(xiàn)這一幀F(xiàn)lag0表示可達(dá),F(xiàn)lag1表示可能不可達(dá),下一幀反過來,下下一幀再反過來。

然后一次GC結(jié)束后,PostCollectGarbageImpl,GatherUnreachableObjects會遍歷所有UObject,把依然有MaybeUnreachableObjectFlag的Object視為垃圾,標(biāo)記上Unreachable Flag。



遍歷完成后,插入到專門儲存待清理對象的GUnreachableObjects數(shù)組中。


2. MarkAsGarbage取代MarkAsPendingKill

UE4對于強(qiáng)刪的Object,比如Actor,會MarkAsPendingKill,UE5把接口改成了MarkAsGarbage:

inline void MarkAsGarbage()
{
check(!IsRooted());

AtomicallySetFlags(RF_MirroredGarbage);
GUObjectArray.IndexToObject(InternalIndex)->SetGarbage();

// If we explicitly marked the object as garbage, remove the async flag so it's visible to the GC
AtomicallyClearInternalFlags(EInternalObjectFlags::Async);
}

對應(yīng)的IsPendingKill判斷也改成了IsValid:

inline bool UKismetSystemLibrary::IsValid(const UObject* Object)
{
return ::IsValid(Object);
}

文末,再次感謝南京周潤發(fā)的分享, 作者主頁:https://www.zhihu.com/people/xu-chen-71-65, 如果您有任何獨(dú)到的見解或者發(fā)現(xiàn)也歡迎聯(lián)系我們,一起探討。(QQ群: 793972859 )。

近期精彩回顧



特別聲明:以上內(nèi)容(如有圖片或視頻亦包括在內(nèi))為自媒體平臺“網(wǎng)易號”用戶上傳并發(fā)布,本平臺僅提供信息存儲服務(wù)。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相關(guān)推薦
熱點(diǎn)推薦
伊朗布什爾核電站再遭襲,國際原子能機(jī)構(gòu)呼吁各方克制

伊朗布什爾核電站再遭襲,國際原子能機(jī)構(gòu)呼吁各方克制

界面新聞
2026-03-25 07:05:11
張雪峰突然走了,怪不得聶圣哲教授在他的直播間“提倡生命在于安靜(不動)”

張雪峰突然走了,怪不得聶圣哲教授在他的直播間“提倡生命在于安靜(不動)”

問道求真
2026-03-25 00:03:39
考研導(dǎo)師張雪峰因跑步心臟聚停離世?網(wǎng)傳特殊治療知情同意書也被流傳出。

考研導(dǎo)師張雪峰因跑步心臟聚停離世?網(wǎng)傳特殊治療知情同意書也被流傳出。

貼小君
2026-03-24 20:41:23
馬競官宣!格列茲曼夏窗離隊(duì),10年奪三冠,211球加冕隊(duì)史射手王

馬競官宣!格列茲曼夏窗離隊(duì),10年奪三冠,211球加冕隊(duì)史射手王

奧拜爾
2026-03-24 22:16:16
賈國龍59元燜面大翻車,換個(gè)馬甲繼續(xù)割韭菜,網(wǎng)友:羅永浩白罵了

賈國龍59元燜面大翻車,換個(gè)馬甲繼續(xù)割韭菜,網(wǎng)友:羅永浩白罵了

壹月情感
2026-03-23 17:04:48
疑似張雪峰送醫(yī)照曝光!躺擔(dān)架仍穿著跑步鞋,上午還進(jìn)行了直播

疑似張雪峰送醫(yī)照曝光!躺擔(dān)架仍穿著跑步鞋,上午還進(jìn)行了直播

萌神木木
2026-03-24 20:44:21
愛上中國!中超最大牌外援妻子:中國人是我見過最有素質(zhì)和教養(yǎng)的

愛上中國!中超最大牌外援妻子:中國人是我見過最有素質(zhì)和教養(yǎng)的

邱澤云
2026-03-24 21:28:50
國務(wù)院令!3月30日起全國施行,土葬合法,殯葬業(yè)暴利徹底涼涼!

國務(wù)院令!3月30日起全國施行,土葬合法,殯葬業(yè)暴利徹底涼涼!

今朝牛馬
2026-03-24 22:13:12
悲催!男方86年,深圳全款車房、無負(fù)債,學(xué)歷中專被女方直接拒絕

悲催!男方86年,深圳全款車房、無負(fù)債,學(xué)歷中專被女方直接拒絕

火山詩話
2026-03-25 06:33:07
“貞潔是女孩最高貴的嫁妝”四川一公交車宣傳標(biāo)語引爭議,工作人員:剛收到相關(guān)反饋,目前正在調(diào)查當(dāng)中

“貞潔是女孩最高貴的嫁妝”四川一公交車宣傳標(biāo)語引爭議,工作人員:剛收到相關(guān)反饋,目前正在調(diào)查當(dāng)中

臺州交通廣播
2026-03-24 21:19:27
歐洲是如何悄然介入美伊戰(zhàn)爭的

歐洲是如何悄然介入美伊戰(zhàn)爭的

澎湃新聞
2026-03-24 20:54:04
大反攻開始!以色列,徹底被打穿了!

大反攻開始!以色列,徹底被打穿了!

大嘴說天下
2026-03-24 22:55:03
這是劉思齊最真實(shí)的樣貌,這可不是演員扮演的,貨真價(jià)實(shí)的照片

這是劉思齊最真實(shí)的樣貌,這可不是演員扮演的,貨真價(jià)實(shí)的照片

TVB的四小花
2026-03-25 05:07:40
我虧掉200萬??!廣東一投資客深夜痛哭,稱對不起家人,是個(gè)廢人

我虧掉200萬啊!廣東一投資客深夜痛哭,稱對不起家人,是個(gè)廢人

火山詩話
2026-03-24 09:15:50
這次破案了,中方追回走私稀土,價(jià)值46個(gè)億,美國砸重金收買內(nèi)鬼

這次破案了,中方追回走私稀土,價(jià)值46個(gè)億,美國砸重金收買內(nèi)鬼

影孖看世界
2026-03-23 23:29:51
德黑蘭街頭忽然空了:巴斯基帳篷被遺棄,民兵連夜跑了,為什么?

德黑蘭街頭忽然空了:巴斯基帳篷被遺棄,民兵連夜跑了,為什么?

老馬拉車莫少裝
2026-03-24 22:55:20
央媒曝張雪峰去世細(xì)節(jié)!倒在跑步機(jī)上,搶救了3個(gè)小時(shí),遺產(chǎn)超8億

央媒曝張雪峰去世細(xì)節(jié)!倒在跑步機(jī)上,搶救了3個(gè)小時(shí),遺產(chǎn)超8億

180視角
2026-03-25 00:22:51
伊朗伊斯蘭革命衛(wèi)隊(duì)責(zé)令一艘擅自通過霍爾木茲海峽船只返航

伊朗伊斯蘭革命衛(wèi)隊(duì)責(zé)令一艘擅自通過霍爾木茲海峽船只返航

財(cái)聯(lián)社
2026-03-25 02:02:18
伊朗稱收到美國信息,“正予以審視”!美國被曝向多國索要保護(hù)費(fèi):花2.5萬億美元結(jié)束戰(zhàn)爭;特朗普為動武辯護(hù):快攔截不住他們的導(dǎo)彈了

伊朗稱收到美國信息,“正予以審視”!美國被曝向多國索要保護(hù)費(fèi):花2.5萬億美元結(jié)束戰(zhàn)爭;特朗普為動武辯護(hù):快攔截不住他們的導(dǎo)彈了

每日經(jīng)濟(jì)新聞
2026-03-24 15:30:45
拉里賈尼上周遇害,伊朗任命繼任者,履歷公布!伊朗媒體發(fā)布視頻:美國油輪被小艇撞擊后爆炸,火光沖天

拉里賈尼上周遇害,伊朗任命繼任者,履歷公布!伊朗媒體發(fā)布視頻:美國油輪被小艇撞擊后爆炸,火光沖天

每日經(jīng)濟(jì)新聞
2026-03-25 00:26:06
2026-03-25 08:59:00
侑虎科技UWA incentive-icons
侑虎科技UWA
游戲/VR性能優(yōu)化平臺
1559文章數(shù) 986關(guān)注度
往期回顧 全部

科技要聞

紅極一時(shí)卻草草收場,Sora宣布正式關(guān)停

頭條要聞

50歲大叔進(jìn)俱樂部 上海多位富婆為他心甘情愿花1500萬

頭條要聞

50歲大叔進(jìn)俱樂部 上海多位富婆為他心甘情愿花1500萬

體育要聞

NBA最強(qiáng)左手射手,是個(gè)右撇子

娛樂要聞

張雪峰經(jīng)搶救無效不幸去世 年僅41歲

財(cái)經(jīng)要聞

張雪峰的多面人生:從寒門導(dǎo)師到教育商人

汽車要聞

尚界Z7雙車預(yù)售22.98萬起 問界M6預(yù)售26.98萬起

態(tài)度原創(chuàng)

房產(chǎn)
教育
數(shù)碼
手機(jī)
軍事航空

房產(chǎn)要聞

北上廣深二手房集體回暖!三月小陽春行情全面兌現(xiàn)

教育要聞

隱圓問題第2講,一個(gè)視頻學(xué)會!

數(shù)碼要聞

Gurman:蘋果近期多款新品“依舊只是小幅升級”

手機(jī)要聞

蘋果iOS/iPadOS 26.4正式版發(fā)布

軍事要聞

以色列媒體:美國計(jì)劃于4月9日結(jié)束對伊朗戰(zhàn)爭

無障礙瀏覽 進(jìn)入關(guān)懷版