Sweep ๋จ๊ณ ๋ถ์
Unreachable ๊ฐ์ฒด ์ฒ๋ฆฌ, BeginDestroy/FinishDestroy ํ๋ฆ, PendingKill/MarkAsGarbage๋ฅผ ๋ถ์ํฉ๋๋ค
Unreachable ๊ฐ์ฒด ์์ง
Mark ๋จ๊ณ ์ดํ ๋๋ฌ ๋ถ๊ฐ๋ฅํ ๊ฐ์ฒด๋ฅผ ์์งํ๋ ๊ณผ์
Sweep ๋จ๊ณ์ ์์
Mark ๋จ๊ณ๊ฐ ์๋ฃ๋๋ฉด EInternalObjectFlags::Unreachable ํ๋๊ทธ๊ฐ ๋จ์ ๊ฐ์ฒด๊ฐ ๊ฐ๋น์ง์
๋๋ค. Sweep ๋จ๊ณ์์๋ ์ด ๊ฐ์ฒด๋ค์ GUObjectArray๋ฅผ ์ํํ๋ฉฐ ์์งํฉ๋๋ค.
UnhashUnreachableObjects()
โ
โโโ GUObjectArray ์ํ (DisregardForGC ์ดํ ์์ญ)
โ โโโ for (each FUObjectItem with Unreachable flag)
โ โโโ ํด์ ํ
์ด๋ธ์์ ์ ๊ฑฐ
โ โโโ ํด๋ฌ์คํฐ์์ ๋ถ๋ฆฌ
โ โโโ GarbageObjects ๋ชฉ๋ก์ ์ถ๊ฐ
โ
โโโ NotifyUnreachableObjects()
โ โโโ ๋ธ๋ฆฌ๊ฒ์ดํธ๋ฅผ ํตํ ์๋ฆผ
โ
โโโ GarbageObjects ๋ชฉ๋ก โ ์๋ฉธ ํ์ดํ๋ผ์ธ์ผ๋ก
// ์๋ฉธ ํ์ดํ๋ผ์ธ
for (each UnreachableObj)
{
UnreachableObj->ConditionalBeginDestroy();
}
UObject์ ์๋ฉธ์ด ์ฌ๋ฌ ๋จ๊ณ๋ก ๋๋ ์ด์ ๋ ๋ ๋๋ง ๋ฆฌ์์ค์ ๊ฐ์ ๋น๋๊ธฐ ์์ ๋๋ฌธ์ ๋๋ค. GPU์์ ์ฌ์ฉ ์ค์ธ ๋ฆฌ์์ค๋ฅผ ์ฆ์ ํด์ ํ๋ฉด ํฌ๋์๊ฐ ๋ฐ์ํ ์ ์์ผ๋ฏ๋ก, BeginDestroy์์ ํด์ ์์ฒญ์ ํ๊ณ FinishDestroy์์ ์ค์ ํด์ ๋ฅผ ํ์ธํฉ๋๋ค.
BeginDestroy์ FinishDestroy
UObject ์๋ฉธ์ ๋ ๋จ๊ณ ํ๋ก์ธ์ค
์๋ฉธ ํ์ดํ๋ผ์ธ ์์ธ
// BeginDestroy - ์๋ฉธ ์์ (์ค๋ฒ๋ผ์ด๋ ๊ฐ๋ฅ)
void UMyObject::BeginDestroy()
{
// 1. ๋น๋๊ธฐ ๋ฆฌ์์ค ํด์ ์์ฒญ
if (RenderResource)
{
RenderResource->ReleaseResource();
// GPU ๋ฆฌ์์ค๋ ๋ค์ ํ๋ ์๊น์ง ์ ํจ
}
// 2. ํ์ด๋จธ, ๋ธ๋ฆฌ๊ฒ์ดํธ ํด์
if (UWorld* World = GetWorld())
{
World->GetTimerManager().ClearAllTimersForObject(this);
}
// 3. ๋ถ๋ชจ ํธ์ถ (ํ์!)
Super::BeginDestroy();
}
// IsReadyForFinishDestroy - ์๋ฃ ํ์ธ
bool UMyObject::IsReadyForFinishDestroy()
{
// ๋น๋๊ธฐ ์์
์๋ฃ ํ์ธ
bool bReady = Super::IsReadyForFinishDestroy();
bReady &= !RenderResource || RenderResource->IsReleased();
return bReady;
}
// FinishDestroy - ์ต์ข
์ ๋ฆฌ
void UMyObject::FinishDestroy()
{
// ์ด ์์ ์์ ๋ชจ๋ ๋น๋๊ธฐ ์์
์ด ์๋ฃ๋จ
delete RenderResource;
RenderResource = nullptr;
Super::FinishDestroy();
// ์ดํ C++ ์๋ฉธ์ ํธ์ถ, ๋ฉ๋ชจ๋ฆฌ ๋ฐํ
}
์๋ฉธ ๋จ๊ณ ํ์ด๋ฐ
| ๋จ๊ณ | ์์ | ์ฉ๋ |
|---|---|---|
ConditionalBeginDestroy |
GC Sweep ์ฆ์ | ์๋ฉธ ์์ ์กฐ๊ฑด ํ์ธ |
BeginDestroy |
๊ฐ์ ํ๋ ์ | ๋น๋๊ธฐ ๋ฆฌ์์ค ํด์ ์์ฒญ |
IsReadyForFinishDestroy |
๋งค ํ๋ ์ ํด๋ง | ๋น๋๊ธฐ ์์ ์๋ฃ ํ์ธ |
FinishDestroy |
Ready ํ ๋ค์ GC | ์ต์ข ๋ฉ๋ชจ๋ฆฌ ํด์ |
~UObject() |
FinishDestroy ์งํ | C++ ์๋ฉธ์ |
PendingKill๊ณผ MarkAsGarbage
UE4์์ UE5๋ก์ ๊ฐ์ฒด ๋งํน ๋ฐฉ์ ๋ณํ
PendingKill์ ์งํ
UE4์์ ์ฌ์ฉ๋๋ PendingKill ์์คํ
์ UE5์์ MarkAsGarbage๋ก ์ ํ๋์์ต๋๋ค. ์ด ๋ณ๊ฒฝ์ GC ์ฑ๋ฅ ํฅ์๊ณผ TObjectPtr ๋์
๊ณผ ๊ด๋ จ์ด ์์ต๋๋ค.
// UE4 ๋ฐฉ์ (Deprecated)
// obj->MarkPendingKill(); // ๋ ์ด์ ์ฌ์ฉํ์ง ๋ง์ธ์
// obj->IsPendingKill(); // Deprecated
// UE5 ๋ฐฉ์
obj->MarkAsGarbage(); // ๊ฐ๋น์ง๋ก ๋งํน
// ์ ํจ์ฑ ํ์ธ
if (IsValid(obj)) // nullptr + Garbage ์ฒดํฌ
{
// ์์ ํ๊ฒ ์ฌ์ฉ
}
// gc.PendingKillEnabled ์ฝ์ ๋ณ์
// 0 (๊ธฐ๋ณธ): MarkAsGarbage ๋ฐฉ์ ์ฌ์ฉ
// 1: ๋ ๊ฑฐ์ PendingKill ๋ฐฉ์ (ํธํ์ฑ)
// TObjectPtr์ ์๋ null ์ฒ๋ฆฌ
UPROPERTY()
TObjectPtr<AActor> MyActor;
MyActor->Destroy(); // MarkAsGarbage + ์๋ฉธ ์์ฒญ
// ์ดํ MyActor๋ ์๋์ผ๋ก null์ฒ๋ผ ๋์ (TObjectPtr)
// raw UObject*์์๋ ์ง์ ํ์ธ ํ์
UE4 ํ๋ก์ ํธ๋ฅผ UE5๋ก ๋ง์ด๊ทธ๋ ์ด์ ํ ๋:
IsPendingKill()โ!IsValid(obj)๋ก ๋ณ๊ฒฝMarkPendingKill()โMarkAsGarbage()๋ก ๋ณ๊ฒฝUObject*โTObjectPtr<T>๋ก ๋ณ๊ฒฝ ๊ถ์ฅgc.PendingKillEnabled=1๋ก ์์ ํธํ ๊ฐ๋ฅ
Incremental Sweep๊ณผ ๋ถ์ฐ ์ฒ๋ฆฌ
Sweep ๋จ๊ณ์ ์๊ฐ ๋ถ์ฐ ์ ๋ต
์ ์ง์ ์๋ฉธ ์ฒ๋ฆฌ
๋๋์ ๊ฐ์ฒด๊ฐ ๋์์ Unreachable๋ก ํ์ ๋๋ฉด, ํ ํ๋ ์์์ ๋ชจ๋ ๊ฐ์ฒด์ BeginDestroy๋ฅผ ํธ์ถํ๋ฉด ์ฌ๊ฐํ ํ๋ ์ ํ์น๊ฐ ๋ฐ์ํฉ๋๋ค. UE5๋ ์ด๋ฅผ ์ฌ๋ฌ ํ๋ ์์ ๋ถ์ฐํฉ๋๋ค.
// IncrementalDestroyGarbage ํจ์ (๋งค Tick ํธ์ถ)
bool IncrementalDestroyGarbage(bool bUseTimeLimit, float TimeLimit)
{
while (GGCObjectsPendingDestruction.Num() > 0)
{
UObject* Obj = GGCObjectsPendingDestruction.Pop();
if (Obj->IsReadyForFinishDestroy())
{
Obj->ConditionalFinishDestroy();
// ๋ฉ๋ชจ๋ฆฌ ํด์
}
else
{
// ์์ง ์ค๋น ์ ๋จ โ ๋ค์ ํ์
GGCObjectsPendingDestructionLater.Add(Obj);
}
// ์๊ฐ ์ ํ ์ฒดํฌ
if (bUseTimeLimit && FPlatformTime::Seconds() > TimeLimit)
return false; // ๋ค์ ํ๋ ์์ ๊ณ์
}
return true; // ์๋ฃ
}
IsReadyForFinishDestroy()๊ฐ false๋ฅผ ๋ฐํํ๋ฉด ํด๋น ๊ฐ์ฒด๋ ๋ค์ ํ๋ ์์ผ๋ก ์ฐ๊ธฐ๋ฉ๋๋ค. ์ด๋ GUObjectArray์ ์ฌ๋กฏ์ ์์ง ํด์ ๋์ง ์์ ์ํ์ด๋ฉฐ, ์ ๊ฐ์ฒด์ ์ฌ์ฌ์ฉํ ์ ์์ต๋๋ค. ๊ณผ๋ํ ์ง์ฐ์ GUObjectArray ์ธ๋ฑ์ค ๊ณ ๊ฐ์ ์ ๋ฐํ ์ ์์ต๋๋ค.
ํต์ฌ ์์ฝ
- Sweep ๋จ๊ณ๋ Unreachable ํ๋๊ทธ๊ฐ ๋จ์ ๊ฐ์ฒด๋ฅผ GUObjectArray์์ ์์งํฉ๋๋ค
- UObject ์๋ฉธ์ BeginDestroy โ IsReadyForFinishDestroy โ FinishDestroy์ ๋น๋๊ธฐ ํ์ดํ๋ผ์ธ์ ๋๋ค
- UE5์์ PendingKill์ MarkAsGarbage๋ก ๋์ฒด๋์์ผ๋ฉฐ, TObjectPtr๊ฐ ์๋์ผ๋ก ์ฒ๋ฆฌํฉ๋๋ค
- IncrementalDestroyGarbage๋ ์๋ฉธ ์์ ์ ์ฌ๋ฌ ํ๋ ์์ ๋ถ์ฐํ์ฌ ํ์น๋ฅผ ๋ฐฉ์งํฉ๋๋ค
- BeginDestroy์์ Super::BeginDestroy() ํธ์ถ์ ์์ผ๋ฉด ๊ฐ์ฒด๊ฐ ์๊ตฌ์ ์ผ๋ก ์๋ฉธ๋์ง ์์ต๋๋ค
๋์ ๊ณผ์
๋ฐฐ์ด ๋ด์ฉ์ ์ง์ ์ค์ตํด๋ณด์ธ์
UObject๋ฅผ ์์๋ฐ์ BeginDestroy, IsReadyForFinishDestroy, FinishDestroy๋ฅผ ์ค๋ฒ๋ผ์ด๋ํ์ธ์. ๊ฐ ํจ์์ ๋ก๊ทธ๋ฅผ ์ถ๊ฐํ๊ณ GC ํธ๋ฆฌ๊ฑฐ ํ ํธ์ถ ์์์ ํ์ด๋ฐ์ ํ์ธํ์ธ์. IsReadyForFinishDestroy์์ false๋ฅผ ๋ฐํํ๋ฉด ์ด๋ป๊ฒ ๋๋์ง ํ ์คํธํ์ธ์.
AActor์์ Destroy()์ MarkAsGarbage()๋ฅผ ๊ฐ๊ฐ ํธ์ถํ ํ, IsValid(), IsPendingKillPending(), HasAnyFlags(RF_BeginDestroyed) ๋ฑ์ ์ํ๋ฅผ ํ๋ ์๋ณ๋ก ๋ก๊ทธ์ ์ถ๋ ฅํ์ฌ ์๋ฉธ ๋จ๊ณ ์ฐจ์ด๋ฅผ ๋น๊ตํ์ธ์.
๋ค์ํ ์์ Unreachable ๊ฐ์ฒด(100, 1000, 10000)๋ฅผ ์๋์ ์ผ๋ก ์์ฑํ๊ณ ์ฐธ์กฐ๋ฅผ ํด์ ํ ํ, stat gc๋ก Sweep ๋จ๊ณ ์๊ฐ์ ์ธก์ ํ์ธ์. ๊ฐ์ฒด ์ ๋๋น Sweep ์๊ฐ์ ์ ํ์ฑ์ ๋ถ์ํ๊ณ gc.MaxObjectsToDestroyPerFrame์ ์ํฅ์ ํ ์คํธํ์ธ์.