✅ 가비지 컬렉터(GC)의 역할이란?
더 이상 사용되지 않는 힙의 메모리를 자동으로 찾아서 정리해주는 시스템
즉,
- 메모리 누수 방지
- 수동으로 메모리 해제하지 않아도 됨 (delete, free 필요 없음)
✅ GC의 동작 기준 (언제 실행되나?)
GC는 명확한 트리거 조건이 있어. 하지만 언제 실행될지 정확히 예측하긴 어려워.
📌 주요 트리거 조건:
- 힙에 여유 공간이 부족할 때
- 새 객체를 new 하려는데 힙에 공간이 부족하면 → GC 실행
- 너무 많은 객체가 생성되었을 때
- 특정 임계치 이상 객체가 쌓이면 → 자동 실행
- 시간이 일정 이상 지났을 때 (백그라운드 GC)
- .NET 런타임이 비활성 시간에 GC를 실행하는 경우
- GC.Collect()를 명시적으로 호출했을 때
- 가능하긴 하지만, 직접 호출은 성능상 좋지 않아서 권장하지 않음
✅ GC는 어떤 기준으로 “삭제할 대상을” 고르나?
GC는 **루트(Root)**에서부터 시작해서, 더 이상 접근할 수 없는 객체를 "가비지"라고 판단해.
🔸 Root란?
- 스택의 지역 변수
- 전역 변수 (static)
- CPU 레지스터에 참조된 객체 등
👉 이 루트에서 참조되고 있지 않은 객체는 **"아무도 쓰고 있지 않다"**고 간주하고 수거함.
✅ GC는 세대를 나눠서 관리한다 (Generational GC)
GC는 성능 최적화를 위해 **객체의 생존 시간을 기준으로 세대(Generation)**를 나눠서 관리해.
세대설명
Gen 0 |
막 생성된 객체. 대부분 여기서 수거됨 (소멸률 높음) |
Gen 1 |
Gen 0을 넘긴 객체들. 조금 더 오래된 객체 |
Gen 2 |
가장 오래 살아남은 객체. 수거 대상에서 가장 오래 남아 있음 |
🔥 대부분의 객체는 Gen 0에서 생기자마자 사라짐
→ 그래서 GC는 Gen 0만 자주 돌리고, Gen 2는 최소한으로만 돌리는 것이 성능에 좋아
✅ 예시 시나리오
void CreateGarbage() { for (int i = 0; i < 100000; i++) { var temp = new object(); // 힙에 생성됨 } }
- 위 코드를 실행하면 object들이 계속 힙에 생성됨
- 일정 수 이상이 되면 → GC가 자동으로 실행되어 참조되지 않는 object들을 회수함
흠...근데 주요 트리거 조건에서 궁금한게 생겼다.
1. 힙에 여유 공간이 부족한 기준
2. 너무 많은 객체가 생성된다는 기준이 어느정도인지
3. 시간이 일정 이상 지났을때의 기준
GPT에게 물어보니 친절하게 설명해줬다.
✅ 1. 힙에 여유 공간이 부족할 때 → GC 실행
🔸 그럼 “여유 공간 부족”의 기준은 뭘까?
❗ 정해진 “절대 크기”는 없음.
대신 세대(Generation)별 할당량과 동적 상황에 따라 달라짐.
📌 핵심 개념: Allocation Budget (할당 예산)
- GC는 Gen 0/1/2 각각에 대해 힙 할당 예산을 갖고 있음
- 이 예산을 초과하면 → GC가 실행됨
- 예산은 GC가 이전에 수거한 양, 메모리 압력, CPU 상황 등을 고려해 동적으로 조절됨
📌 예시:
- Gen 0 힙이 100KB 할당 예산을 갖고 있고, 지금까지 95KB 사용됨
- 그런데 new로 10KB 객체를 만들려고 하면 → 예산 초과
→ ✅ GC가 Gen 0에서 먼저 동작
💡 즉, **"GC는 얼마나 썼냐"보다는 “할당하려는 크기 + 예산 초과 여부”**로 판단한다는 점이 중요!
✅ 2. 너무 많은 객체가 생성되었을 때 → GC 실행
🔸 기준은?
역시 정해진 개수 기준은 없음.
GC는 “많이 생성되었다”는 양적 기준보다, “할당 속도와 생존률”을 중요하게 본다.
📌 GC는 객체 생존률을 보고 판단
- GC는 "이번에 생성된 객체들이 얼마나 빨리 죽는가?"를 본다
- 생성만 많이 되고 죽지 않으면, 세대(Gen) 올라가며 압력 증가
- 그럼 GC는 더 자주 실행됨
📌 예시 상황:
- for 루프에서 수만 개의 new 연산이 빠르게 실행됨
- 대부분은 Gen 0에서 할당되고, 금방 참조가 끊어짐
→ GC는 빠르게 Gen 0만 자주 실행
✅ 3. 시간이 일정 이상 지났을 때 (백그라운드 GC)
🔸 기준은?
⏱️ 이건 명확히 시간 초 단위로 정해져 있는 게 아님
대신 GC가 백그라운드 스레드에서 “유휴 시간”을 감지할 때 실행됨
📌 백그라운드 GC가 동작하는 상황:
- 앱이 입력을 기다리거나, 유저가 아무것도 하지 않을 때
- GC는 작업이 적은 타이밍에 자동 실행 → 앱이 바쁠 땐 일부러 안 함
✅ 보충 정리: GC의 실행 타이밍은 “예측 불가능하지만, 패턴은 있다”
트리거 상황설명
힙 공간 부족 |
할당 예산 초과 시 |
객체 생성량 급증 |
단기간에 많은 new → GC 주기 짧아짐 |
유휴 시간 감지 |
백그라운드에서 앱 부하 없을 때 |
명시적 호출 |
GC.Collect() → 강제 실행 (권장 ❌) |
✅ 그럼 우리가 할 수 있는 건?
- GC 타이밍을 완전히 제어할 수는 없지만,
- 다음과 같은 습관을 통해 예측 가능하고 최소화된 GC 동작을 만들 수 있어:
🔹 GC 최적화 팁 (특히 Unity 기준)
- Update()에서 new 피하기
- List.Clear()로 컬렉션 재사용
- Object Pooling 활용
- string 연결 시 StringBuilder 사용
- 큰 객체는 필요할 때만 생성 & 사용 후 null 처리
✅ 요약
질문정리
1. 힙 공간 부족 기준은? |
할당 예산 초과가 기준 (세대별 동적 조절) |
2. “많은 객체 생성” 기준은? |
객체 생성 속도 + 생존률을 종합적으로 판단 |
3. “시간이 지나면 실행” 기준은? |
앱이 유휴 상태일 때 백그라운드에서 감지하여 실행 |
그렇다고 한다! 1번은 정해진 절대적인 크기는 없다고하니 음.. 대신 세대별 할당량과 동적 상황에 달라지고 2번은 객체의 생존률을 보고 판단하고 3번은 유휴 시간을 감지하면 한다니
컴퓨터마다 다 성능도 다르고 코딩할때도 다 설정값이 다르니 계산된 값에 따라 컴퓨터에 따라 달라지지 않을까 싶다. 웬만하면 비슷할 지도?
예전에 회사에서 면접을 볼떄 가비지컬렉터가 작동할떄 언제 자동으로 작동되냐? 라고 질문을 받은적이 있었다. 근데 나도 생각해보니 자동으로 처리해주는것만 알았지 어떻게 자동으로 처리해주는지에 대한 내용은 잘 몰랐다. 스파르타 임예찬 튜터님께 물어보니 잘 설명해주셨고 나머지 더 자세한 부분은 GPT한테 물어보니 잘 대답해줬다. 궁금내용 해결!