GC에 대해 자세히 알아보자.
Destroy를 했는데 메모리가 남아있는것 처럼 느껴졌다.
참조 타입을 공부하다 보니 객체가 언제 사라지는지 의문이 들었다.
지금까지는 GC가 자동으로 메모리 정리를 해준다 라고 얕게 알고 있었다.
언제 실행되고 어떻게 관리가 되는지 궁금해졌다.
Garbage Collection은 메모리 관리 기법의 하나 이다.
"자동메모리 관리자" 역할로 메모리 할당 및 해제를 관리한다.
기본적인 동작 방식
1. 현재 수행중인 스레드를 모두 중단하고 GC스레드를 활성화 한다.
2. 사용중인 객체 참조그래프 : 루트를 생성한다.
3. 힙 메모리의 모든 데이터는 처음에 쓰레기로 간주한다.
4. GC는 루트를 참조하여 관계있는 데이터를 쓰레기에서 제외한다.
5. 쓰레기 데이터를 삭제하고 현재 사용중인 객체의 위치를 재조정(메모리 컴팩션)한다.
객체는 "참조가 하나도 없을 때" 만 GC 대상이 된다.
class Player { public int hp; }
void MakePlayer()
{
Player p = new Player();
}
MakePlayer();
// 여기서 p는 사라졌지만, 객체는 "GC 대상"일 뿐이다.
p라는 참조변수는 "스택"에, Player 객체는 "힙"에 생성된다.
이 둘은 완전히 다른 존재이다.
p = 스택(Stack)에 있는 지역 변수
new Player() = 힙(Heap)에 있는 실제 객체
스택은 함수가 끝나면 통째로 정리된다.
MakePlayer() 가 끝나는 순간 Stack Frame 자체가 제거된다.
따라서 p는 함수가 끝남과 동시에 제거되어 더이상 Player를 참조하지 않는다.
그럼 힙에 있는 객체는 참조가 없어서 사라져야 정상이다.
하지만 객체는 바로 사라지지 않는다.
현재 상태는 GC 대상 이라고 말한다.
"GC 대상" 과 "실제 메모리 해제" 는 다르다.
- ❌ 객체는 GC대상이 되었다고 바로 삭제되지 않는다.
- ❌ Destroy를 호출한다고 메모리가 즉시 해제되는건 아니다.
- GC는 런타임이 필요하다고 판단할 때만 실행된다.
- 언제 도는지는 개발자가 알 수도, 제어할 수도 없다.
즉, GC는 언제든지 돌 수 있지만 대상이 된다고 해서 바로 작동하지 않는다.
- 새 객체를 할당하려는데 공간이 부족할 때
- 세대 (Generation) 기준이 찼을 때 (C# GC는 세대별 GC를 쓴다)
- 명시적으로 GC.Collect()를 호출했을 때
❗ 명시적 호출은 즉시 GC를 실행시켜 Stop-The-World가 발생한다. ❗
❗ 게임/실시간에선 앱에선 거의 쓰면 안된다 ❗
Destroy는 Unity 엔진 오브젝트 제거 예약 이다. C# 메모리 해제가 아니다.
1️⃣ 이벤트 구독 해제를 안 한 경우
2️⃣ static / Singleton이 참조하는 경우
3️⃣ Coroutine이 객체를 붙잡고 있는 경우
4️⃣ Manager가 리스트로 들고 있는 경우 -> Remove 안하면 참조가 유지된다.
- Destroy는 메모리 해제가 아니다. 오브젝트 제거 예약이다.
- 참조가 사라져야 GC 대상이 된다.
- Unity 개발은 객체 생성보다 객체 수명을 중점으로 설계해야 한다.
헉 가비지 콜렉터가 메모리를 즉시 해제하는게 아니였군요