Note

GC에 대해

by 달쿨

GC란

GC란 가비지 컬렉션(Garbage Collection)을 의미하며 Heap 메모리 영역에서 더는 사용되지 않는 메모리를 정리하는 프로세스를 말한다. (Heap 영역의 메모리는 Stack 영역과 달리 메서드 실행이 종료되더라도 삭제되지 않기 때문에.) 여전히 참조되고 있는 객체와 더 이상 참조되지 않는 객체를 구분해 참조되지 않는 객체를 정리한다.

 

Heap 영역

GC의 대상이 되는 Heap 영역은 효율적인 메모리 관리를 위해, 객체의 생존 기간에 따라 물리적인 Heap 영역을 Young 영역과 Old 영역으로 나누어 설계하였다.

이는 Weak Generational Hypothesis(약한 세대 가설)을 바탕으로 나누어 진 것인데, 해당 가설의 핵심은 ① 대부분의 객체는 할당된 후 얼마 지나지 않아 금방 접근 불가능 상태가 되며, ② 오래된 객체가 새로운 객체를 참조하는 일은 거의 없다는 것으로, 결국 객체는 대부분 일회용으로 메모리에 오래 남아있는 경우는 드물다는 것이다.

그래서 생존 기간에 따라 새로운 객체들이 할당되는 Young 영역과, Young 영역에서 오랫동안 살아남은 객체들이 복사되어 존재하는 Old 영역으로 설계되었다. 이 때 Young 영역에 대한 GC를 Minor GC, Old 영역에 대한 GC를 Major GC라고 한다. (*예외적으로 Old 영역의 객체가 Young 영역의 객체를 참조하는 경우를 대비해 Old 영역에 카드 테이블이 존재한다.)

 

GC 기본 동작

두 영역의 메모리 구조가 다르기 때문에 Minor GC와 Major GC의 세부적인 동작 방식은 다르지만, 기본적으로 Stop The World와 Mark and Sweep의 동작을 한다.

 

Stop The World는 GC를 수행하기 위해 JVM이 애플리케이션 실행을 멈추는 현상을 의미한다. 즉, GC가 수행될 때는 GC 관련 스레드를 제외한 모든 스레드는 작업이 중단되고, GC가 완료된 후에야 작업이 재개되는 것이다. 이로 인해 성능 문제가 발생할 수 있기 때문에 GC 튜닝을 통해 해당 시간을 줄이는 최적화 작업을 필요로 한다.

 

Mark and Sweep은 기본적인 GC의 알고리즘으로, 말 그대로 Mark(식별)와 Sweep(제거) 과정을 통해 메모리를 정리한다. 첫 번째 단계인 마킹 단계에서는 GC 루트로부터 시작해 참조 체인을 따라가며 각각이 어떤 객체를 참조하고 있는지를 찾아서 마킹한다. 즉, 살아있는 객체를 식별한다. 그 다음 스윕 단계에서는 마킹되지 않은 객체를 힙 메모리에서 제거하고 해당 메모리를 재사용 가능하도록 한다. 이를 통해 더 이상 참조되고 있지 않는 상태의 객체는 삭제되게 된다. (*이 때 GC 루트는 힙 메모리 영역을 참조하는 스레드의 스택 변수, 클래스의 정적 변수 등으로 구성된다.)

 

Minor GC

Minor GC는 Young 영역에 대한 GC로, Young 공간이 Old 공간에 비해 상대적으로 작기 때문에 적은 시간이 걸려서 Minor GC라 불린다.

 

먼저 Young 영역은 더 효율적인 GC를 위해 Eden, survivor 0, survivor 1의 3가지 영역으로 나누어진다. Eden 영역은 new를 통해 새로 생성되는 객체가 할당되는 영역이며 survivor 영역은 최소 한번의 GC 과정에서 살아남은 객체가 존재하는 영역을 의미한다.

 

처음 생성된 객체는 Eden 영역에 위치하게 되는데, 객체가 계속 생성되어 Eden 영역이 꽉 차게 되면 Minor GC 가 실행되어 사용되지 않는 객체의 메모리가 해제된다. 그리고 이 때 살아남은 객체가 survivor 영역으로 이동하게 되고 각 객체의 age(생존 횟수 값)을 증가시켜준다. 또 Eden 영역이 가득차면 GC가 실행되는데 이 때 살아남은 객체는 비어있는 다른 survivor 영역으로 이동한다. 그리고 Eden 영역과 함께 기존 survivor 영역도 비게 된다. 즉 옮겨진 survivor 영역에는 age가 다른 객체들이 각기 존재하게 된다. 이렇게 반복하다 보면 특정 객체들은 age 임계치에 도달하게 되는데, 이 객체들이 Old 영역으로 이동(Promotion)하게 되는 것이다.

 

Major GC

이렇게 계속 Young 영역에서 Old 영역으로 이동하게 되면 결국 Old 영역의 메모리가 부족해지는데 이 때 Major GC가 발생하게 된다. 그래서 Old 영역에 있는 모든 객체들을 검사하는데, Old 영역은 Young 영역에 비해 큰 공간을 가지기 때문에 Minor GC 보다 훨씬 느리다. 즉 Stop-The-World로 인한 문제가 두드러지게 되는데 이를 해결하기 위해 다양한 GC 알고리즘이 등장하게 되었다.

 

GC 알고리즘

GC의 주요 알고리즘으론 Serial GC, Parallel GC, G1 GC, ZGC 등이 있다.

 

Serial GC는 단일 스레드만을 이용해 동작하며 Young 영역은 Mark-Sweep 방식으로 수행되지만, Old 영역에서는 Mark-Sweep-Campact 알고리즘이 수행된다. Campact 과정은 Heap 영역을 정리하기 위한 단계로 Sweep 후에 흩어진 객체들을 힙의 앞부분부터 채워서 객체가 존재하는 부분과 존재하지 않은 부분으로 나누어 압축하는 것이다. CPU 코어가 1개일 때 사용하기 위해 개발되었기 때문에 코어가 여러 개인 서버에서 사용하는 것은 피해야 한다.

 

Parallel GC는 Throughput GC로도 알려져 있으며, 기본적인 동작은 Serial GC와 동일하지만, 단일 스레드를 사용했던 Serial GC와는 다르게 Minor GC를 멀티 스레드로 Parallel(병렬)하게 수행함으로 GC의 오버헤드를 줄여주는 방식이다. 최대 지연 시간이나 GC 수행 스레드 갯수 등을 설정할 수 있다.

 

이를 좀 더 개선한 버전이 Parallel Old GC로,이전 방식에선 Old 영역 GC가 단일 스레드로 이루어졌지만, 해당 버전은 멀티 스레드가 Old 영역을 논리적으로 균일하게 나눈 Region 단위로 GC를 수행한다. Mark-Summary-Compact 라는 방식을 사용한다.

 

CMS GC는 Concurrent Mark & Sweep의 약자로 stop-the-world 시간을 최대한 줄이기 위해 고안된 GC이다. Mark Sweep 알고리즘이 애플리케이션 작업과 Concurrent(동시)하게 수행되는 건데, 자원이 GC를 위해서도 사용되므로 응답이 느려질 순 있지만 멈추지는 않는다. 하지만 다른 GC보다 메모리와 CPU를 더 많이 필요로 하고, Compaction 작업이 없기 때문에 단편화가 계속 일어나다가 심해질 경우 Compaction을 진행하는데, 이 과정에서 STM이 더 오래걸릴 수 있다. (결국 Java14에서 사용이 중단되었다.)

 

이를 대체하기 위해 만들어진 방법이 G1(Garbage First) GC 방식이다. 기존 GC의 Heap 영역을 Young과 Old의 영역으로 나누어 사용하는 방식이 아닌, Region 이라는 논리적인 단위로 힙 영역을 균등하게 나누고, 각 지역의 상태에 따라 특정 역할(Eden,Survivor,Old 등을 동적으로 부여하는 방식이다.

 

 

 

[참고 링크]

'공부' 카테고리의 다른 글

스레드에 대해  (0) 2026.03.31
JVM에 대해  (0) 2026.03.29
AOP에 대해  (0) 2026.03.18
역색인(Inverted Index) 구조  (0) 2025.04.24
RabbitMQ 개념  (3) 2025.01.14

블로그의 정보

note

달쿨

활동하기