[궁금시리즈] 2-8. 객체는 언제 생성되고 언제 사라질까?
C#에서는 객체를 생성하는 것이 매우 쉽다.
Player player = new Player();
new 키워드 하나만 작성하면 객체가 만들어진다. 반대로 객체를 삭제하는 코드는 존재하지 않는다.
delete player;
같은 문법도 없다.
그렇다면 객체는 언제 생성되고, 언제 메모리에서 사라질까?
이번 글에서는 객체의 생명주기(Lifetime)를 따라가며 메모리 관리가 실제로 어떻게 이루어지는지 알아보자.
객체 생성
객체는 new를 호출하는 순간 생성된다.
Player player = new Player();
실행 순서는 다음과 같다.
new 호출
↓
Heap에 메모리 확보
↓
생성자 실행
↓
참조 반환
즉, new는 단순히 생성자를 호출하는 것이 아니다.
먼저 Heap에 메모리를 할당하고, 그 위에서 생성자가 실행된다.
생성자(Constructor)
class Player
{
public Player()
{
Console.WriteLine("생성");
}
}
Player player = new Player();
순서는
Heap 메모리 생성
↓
필드 기본값 초기화
↓
생성자 실행
이다.
생성자는 객체를 만드는 것이 아니라 이미 생성된 객체를 초기화하는 역할을 한다.
참조가 생긴다
Player player = new Player();
메모리는
Stack
player
↓
Heap
Player
이 된다.
여기서 중요한 것은 변수는 객체를 가지고 있는 것이 아니라 객체를 참조한다는 점이다.
참조가 하나 더 생기면?
Player a = new Player();
Player b = a;
메모리는
a ─┐
│
b ─┘
↓
Player
객체는 하나다.
참조만 늘어난 것이다.
참조가 없어지면?
Player player = new Player();
player = null;
이제
Player
참조 없음
상태가 된다.
하지만 객체는 즉시 삭제되지 않는다.
GC가 나중에 회수한다.
메서드가 끝나면?
void Create()
{
Player player = new Player();
}
메서드 종료 ↓ Stack 제거 ↓ Heap 객체는 참조 없음 ↓ GC 대상
즉, 지역 변수는 사라졌지만 객체는 GC가 실행될 때까지 Heap에 남아 있다.
객체가 살아남는 경우
Player Create()
{
return new Player();
}
Player player = Create();
메서드는 종료되었다.
하지만
player
↓
Player
참조가 존재한다.
따라서 객체는 계속 살아있다.
객체가 오래 살아있는 경우
예를 들어
static Player player;
또는
Singleton.Instance
같은 객체는 프로그램이 종료될 때까지 살아있는 경우가 많다.
이러한 객체는 Gen2까지 올라가는 경우가 많다.
객체 수명(Lifetime)
객체는
new
↓
Heap 생성
↓
참조 생성
↓
사용
↓
참조 제거
↓
GC
↓
메모리 회수
라는 생명주기를 가진다.
왜 객체 생명주기가 중요할까?
실무에서 대부분의 성능 문제는 객체 생성 횟수와 객체 수명 때문에 발생한다.
예를 들어
Update()
{
new Bullet();
}
매 프레임마다 객체를 생성하면
생성
↓
Gen0
↓
GC
↓
생성
↓
GC
가 반복된다.
반면 Object Pool을 사용하면 생성과 삭제를 반복하지 않아 GC 부담이 크게 줄어든다.
객체를 빨리 삭제하면 좋을까?
많은 초보 개발자는
player = null;
을 자주 하면 메모리가 절약된다고 생각한다.
하지만 그렇지 않다.
GC는 참조가 없어졌다고 즉시 실행되지 않는다.
오히려 의미 없는 null 대입은 코드를 복잡하게 만들 수 있다.
정말 필요한 경우가 아니라면 지역 변수는 자연스럽게 스코프를 벗어나도록 두는 것이 좋다.
마무리
객체는 new를 호출하는 순간 Heap에 생성되고, 참조가 모두 사라진 뒤 GC에 의해 회수된다. 생성자, 참조, GC는 각각 독립적인 역할을 담당하며, 이 흐름을 이해하면 메모리 관리의 전체 그림을 볼 수 있다.
특히 성능이 중요한 프로그램에서는 객체를 얼마나 자주 생성하는지, 얼마나 오래 유지하는지가 GC 성능에 직접적인 영향을 미친다.
다음 글에서는 Object Pool은 왜 GC 최적화의 핵심 기법일까?를 알아보며 객체 재사용 전략을 살펴보겠다.
핵심 정리
- new는 Heap에 메모리를 할당한 뒤 생성자를 실행한다.
- 생성자는 객체를 만드는 것이 아니라 초기화한다.
- 객체는 참조가 하나라도 남아 있으면 살아있다.
- 참조가 모두 사라져야 GC 대상이 된다.
- 메서드 종료와 객체 삭제는 같은 개념이 아니다.
- 객체 생성 횟수와 생명주기는 GC 성능에 큰 영향을 준다.
- 불필요한 null 대입보다 적절한 객체 생명주기 관리가 더 중요하다.