FireDrago

[기술면접] 1. Young GC와 Full GC의 차이가 뭐죠? 본문

백엔드/Java

[기술면접] 1. Young GC와 Full GC의 차이가 뭐죠?

화이용 2026. 5. 16. 13:14

 

최근 기술면접을 다녀왔다.

이때까지 기술면접에서 받았던 질문들을 공부하고 정리하는 시리즈를 포스팅해보려고 한다.
첫번째 질문은 "Young GC와 Full GC의 차이"이다.

 

GC(Garbage Collection)가 뭔데?

'자바의 특징'을 검색하면 바로 자동 메모리 관리가 뜬다.

자바가 처음 세상에 나올 당시 기존의 언어(c,c++)들은 개발자가 수동으로 메모리를 관리해야 했다.

수동으로 메모리를 관리하는 프로그래밍 언어는 메모리를 효율적으로 최적화 할수 있지만,

동시에 메모리 누수같은 치명적인 버그를 발생시킬 위험도 그만큼 높았다.

 

그래서 자바는 GC(Garbage Collector)를 도입하여

개발자는 오로지 비지니스 로직에만 집중하고 시스템 차원에서 메모리를 자동으로 관리할 수 있도록 했다.

 

Young / Old Generation 

힙 메모리 구조

자바 메모리 구조는 Young GenerationOld Generation으로 구분되어 있다.

애플리케이션을 실행하면 끝까지 살아남는 무거운 객체(스프링의 @Service, @Controller 빈 등)와
0.1초 만에 쓰임을 다하고 버려지는 가벼운 객체(API 응답용 DTO, 임시 문자열 등)가 생성된다.

만약 이들이 하나의 공간에 마구 뒤섞이게 된다면 어떻게 될까?

 

단일메모리 구조의 한계 : 메모리 단편화

상대적으로 가볍고 수명주기가 짧은 객체들이 삭제되면서 쥐파먹은것처럼 빈공간이 발생한다.

전체 남은 용량은 충분한데 연속된 빈 공간이 없어서 새로운 객체를 할당하지 못하는 메모리 단편화 문제가 발생한다.

 

단일 메모리 구조의 한계 : STW(Stop-The-World)

흩어진 객체들을 한곳에 모으기 위해 압축 작업을 해야한다.

그런데 전체 메모리를 하나의 통으로 쓰면,

청소를 할 때마다 수 GB에 달하는 거대한 메모리 전체를 스캔하고 위치를 조정해야 한다.

청소하는 동안 서버가 멈추는 시간(STW)이 기하급수적으로 늘어난다.

 

 

약한 세대 가설 (Weak Generational Hypothesis)

단일 메모리 구조의 위와 같은 한계와 함께 JVM 연구자들은 두가지 가설을 바탕으로 메모리 구조를 설계했다.

 

  1.  대부분의 객체는 생성된 후 금방 참조가 사라져 쓸모없는 객체가 된다.
  2. 오래된 객체가 젊은 객체를 참조하는 일은 드물다.

어차피 생성되는 객체 대부분이 쓸모없어 진다면, 이 객체들만 따로 모아서 한번에 밀어버리자! 

이것이 메모리를 Young / Old 각각 구분하는 이유다.

 

Young Generation  구조

금방 죽을 객체들이 모이는 Young Generation은 어떻게 청소 효율을 극대화했을까?

Young 영역은 단순히 하나의 공간이 아니라, 다시 3개의 구역으로 쪼개진다.

 

  • Eden : 최초로 생성된 객체가 할당되는 영역이다.
  • Survivor0 / Survivor1 : GC후 살아남은 객체들이 이동하는 영역이다. 

 

Young GC의 동작 원리

Eden 구역이 꽉 차게 되면 Young GC(Minor GC)가 발동한다.

이때 메모리 단편화를 막기 위해 아주 무식하고 확실한 복사 알고리즘을 사용한다.

 

  1. Eden 구역과 Survivor(예 Survivor0) 구역에서 살아남은 객체를 골라낸다.
  2. 반대 Survivor(Survivor1) 구역에 빈공간이 없도록 잘 압축하여 이동시킨다.
  3. Eden 구역과 Survivor0의 공간을 한 번에 싹 정리한다. (효율적)

이 과정 덕분에 쓰레기를 일일이 찾아 지울 필요 없이,

살아남은 소수만 대피시키고 방을 통째로 날려버리기 때문에  청소 속도가 압도적으로 빠르다.

Survivor 구역이 반드시 2개여야 하는 이유도 바로 이 무한 핑퐁(대피소 이동)을 위해서이며,

둘 중 하나는 무조건 100% 텅 빈 상태를 유지해야 한다.

 

 

주의점: 조기 승급 (Premature Promotion)문제

살아남은 객체들은 Young GC를 겪을 때마다 나이(Age)를 1살씩 먹고,
특정 나이에 도달한 객체는 Old Generation으로 승급하게 된다. (Promotion)

 

하지만 만약 Survivor 공간이 너무 좁다면 어떻게 될까?

나이를 덜 먹은 젊은 객체들이 갈 곳이 없어 억지로 Old 영역으로 승급하게 된다.

이를 조기승급이라고 한다.

 

이런 객체들이 Old 영역으로 쫓겨나는 순간 끔찍한 Full GC를 유발하는 시한폭탄이 된다.

따라서 애플리케이션의 특성에 맞게 Eden과 Survivor의 공간 비율을 튜닝하여 조기 승급을 막는 것이 매우 중요하다.

 

 

Old Generation과 STW 문제

Young 영역에서 끈질기게 살아남은 객체들과,

Survivor이 좁아 억지로 밀려난 조기 승급 객체들이 차곡차곡 쌓이다 보면,

결국 거대한 Old 영역마저 꽉 차게 되는 순간이 온다.

 

이때 JVM은 힙(Heap) 메모리 전체를 뒤집어엎는 대청소, Full GC (또는 Major GC)를 발동시킨다.

그리고 이 순간, 개발자들이 가장 두려워하는 현상이 발생한다.

 

 

Stop-The-World (STW)

안전하게 메모리를 청소하고 흩어진 객체들의 빈 공간을 압축(Compaction)하려면,

청소하는 동안 새로운 객체가 생성되거나 참조 위치가 바뀌면 안된다.

그래서 JVM은 GC를 수행하는 스레드를 제외한 애플리케이션의 모든 스레드를 일시 정지 시킨다.

이를 Stop-The-World(STW)라고 한다.

 

Youg GC도 물론 STW를 발생시킨다. 하지만 빠르게 끝나기 때문에 거의 체감하지 못한다.

하지만 덩치가 큰 Old 영역을 뒤지는 Full GC는 다르다.

길게는 수 초동안 서버가 완전히 멈춰버린다.

 

 

서버 메모리를 빵빵하게 늘리면?

어차피 메모리 공간이 부족해서 생기는 문제라면,

AWS에 돈을 더 주고 서버 메모리(RAM)를 확 늘려버리면 해결되지 않을까?

 

힙 공간이 넓어졌으니, 쓰레기가 꽉 차서 Full GC가 터질 때까지 걸리는 시간이 엄청나게 길어진다.

하루에 10번 터지던 Full GC를 하루 1번으로 줄일 수 있다. 

 

문제는 한 번 청소할 때 걸리는 시간이다.

STW 시간이 5초, 10초로 무지막지하게 늘어난다.

사용자 입장에서 하루 10번 0.5초씩 살짝 버벅이는 서버는 견딜 수 있지만,

하루 1번 10초 동안 완전히 뻗어버리는 서버는 버틸 수 없다.

결국 메모리를 무작정 늘리는 것(Scale-up)은 과거의 GC 방식에서는 오히려 더 큰 재앙을 불러왔다.

 

GC 튜닝과 최신 GC

결국 "Young GC와 Full GC의 차이가 무엇인가?"라는 질문은,

단순히 메모리 구조를 외우고 있는지 묻는 것이 아니다.

애플리케이션의 멈춤(STW) 현상을 어떻게 통제할 것인가?를 묻는 시스템 아키텍처 관점의 질문이다.

이를 해결하기 위한 실무적인 GC 튜닝과 최적화의 방향성은 크게 두 가지로 나뉜다.

 

1. 공간 튜닝: "죽을 객체는 Young에서 확실히 죽게 만들어라"

가장 기본적이고 중요한 튜닝은 힙 내부의 비율(공간)을 조절하는 것이다.

앞서 살펴보았듯, 가장 치명적인 재앙은 Survivor이 좁아

금방 죽을 젊은 객체들이 Old 영역으로 밀려나는 조기 승급이다.

 

따라서 애플리케이션의 특성에 맞춰 힙(Heap) 메모리의 전체 크기뿐만 아니라,

Young과 Old 영역의 비율, 그리고 Eden과 Survivor 영역의 비율을 세밀하게 조절해야 한다.

실시간으로 쏟아지는 웹소켓 채팅 메시지나 API 응답 DTO처럼 짧게 쓰이고 버려지는 임시 객체가 많은 시스템일수록,

Young 영역(특히 Survivor 공간)을 넉넉하게 확보하여 Full GC 발생 자체를 최대한 억제하는 것이 핵심이다.

 

 

2. 알고리즘의 발전: G1GC와 ZGC 

메모리(RAM)를 크게 늘리면 STW 시간이 길어지는 딜레마는 어떻게 해결했을까?

현대 자바는 패러다임을 바꾸는 새로운 GC 알고리즘들을 도입했다.

 

최신 알고리즘들은 거대한 old 영역을 한 번에 다 청소하려다 시간을 지체하지 않는다.

대신 전체 메모리를 바둑판처럼 수많은 작은 구역(Region)으로 쪼갠다.

그리고 전체를 멈추고 스캔하는 대신, "쓰레기가 가장 많이 쌓인 구역(Garbage First)부터 조금씩,

애플리케이션 실행과 동시에 틈틈이 치우는 방식"을 사용한다.

덕분에 수십, 수백 GB의 거대한 메모리를 사용하면서도 STW 시간을 1ms ~ 10ms 이하의 찰나의 순간으로 통제할 수 있다.