말감로그

[Java] 가비지 컬렉션(GC) 동작 원리 본문

언어

[Java] 가비지 컬렉션(GC) 동작 원리

habbn 2025. 8. 9. 17:12
728x90

Garbage Collection(GC)

가비지 컬렉션은 힙 영역에서 동적으로 할당했던 메모리 중 필요 없게 된 메모리를 주기적으로 제거하는 프로세스이다.

 

개발자 입장에서 메모리 관리, 누수 문제에 대해 관리하지 않아도 되어 오롯이 개발에만 집중할 수 있다는 장점이 있다.

하지만, 메모리가 언제 해제되는지 정확하게 알 수 없어 제어하기 힘들며, 가비지 컬렉션이 동작하는 동안에는 다른 동작을 멈추기 때문에 오버헤드가 발생되는 문제점이 있다. -> Stop The World

(*오버헤드 : 특정 기능을 수행하는 데 드는 간접적인 시간, 메모리 등 자원)


가비지 컬렉션 대상

객체에 레퍼런스가 있다면 Reachable로 구분되고, 객체에 유효한 레퍼런스가 없다면 Unreachable로 구분해버리고 수거해버린다.

 

1. Reachable : 객체가 참조되고 있는 상태

2. Unreachable : 객체가 참조되고 있지 않은 상태 (GC의 대상이 됨)

 

생성된 힙 영역의 객체들이 메서드가 끝나는 등의 이벤트로 인해 힙 영역 객체의 메모리 주소를 가지고 있는 참조 변수가 삭제되는 현상이 발생하면, 참조하고 있지 않은 객체(Unreachable)가 발생하면서 GC가 제거해주는 것이다.

 


힙 메모리 구조

힙 영역은 처음 설계될 때 다음의 2가지를 전제로 설계되었다.

 

1. 대부분의 객체는 금방 접근 불가능한 상태(Unreachable)가 된다.

2. 오래된 객체에서 새로운 객체로의 참조는 아주 적게 존재한다.

 

즉, 객체는 대부분 일회성이며, 메모리에 오랫동안 남아있는 경우는 드물다는 것이다. 그렇기 때문에 객체의 생존 기간에 따라 물리적인 힙 영역을 나누게 되었고 Young, Old 총 2가지 영역으로 설계되었다.

 

 

Young 영역(Young Generation)

 

- 새롭게 생성된 객체가 할당되는 영역

- 대부분의 객체가 금방 Unreachable 상태가 되기 때문에, 많은 객체가 Young 영역에 생성되었다가 사라진다.

- Young 영역에 대한 가비지 컬렉션을 Minor GC라고 부른다.

 

Old 영역(Old Generation)

- Young 영역에서 Reachable 상태를 유지하여 살아남은 객체가 복사되는 영역

- Young 영역보다 크게 할당되며, 영역의 크기가 큰 만큼 가비지는 적게 발생한다.

- Old 영역에 대한 가비지 컬렉션을 Major GC라고 부른다.

 

 

힙 영역은 더욱 효율적인 GC를 위해 Young 영역을 3가지 영역(Eden, Survivor 0, Survivor 1) 으로 나눈다.

 

 

Eden

- 새로 생성된 객체가 할당되는 영역

 

Survivor 0 / Survivor 1

- 최소 1번의 GC 이상 살아남은 객체가 존재하는 영역

- Survivor 0 또는 Survivor 1 둘 중 하나는 꼭 비어 있어야 한다. 

 

이렇게 하나의 힙 영역을 세부적으로 쪼갬으로써 객체의 생존 기간을 면밀하게 제어하여 GC를 보다 정확하게 불필요한 객체를 제거하는 프로세스를 실행하도록 한다.


가비지 컬렉션의 동작 방식

 

Young 영역과 Old 영역은 서로 다른 메모리 구조로 되어 있기 때문에, 세부적인 동작 방식은 다르다.

하지만 기본적으로 가비지 컬렉션이 실행된다고 하면 다음의 2가지 공통적인 단계를 따르게 된다.

 

1. Stop The World

2. Mark and Sweep

 

1. Stop The World

가비지 컬렉션을 실행하기 위해 애플리케이션의 실행을 멈추는 작업이다.

GC가 실행될 때는 GC를 실행하는 쓰레드를 제외한 모든 쓰레드들의 작업이 중단되고, GC가 완료되면 작업이 재개된다. 당연히 모든 쓰레드들의 작업이 중단되면 애플리케이션이 멈추기 때문에, GC의 성능 개선을 위해 튜닝을 한다고 하면 보통 Stop the World 시간을 줄이는 작업을 하는 것이다.

 

2. Mark and Sweep

Mark-Swep 이란 다양한 GC에서 사용되는 객체를 솎아내는 내부 알고리즘이다.

 

가비지 컬렉션이 될 대상 객체를 식별(Mark)하고 제거(Sweep)하며 객체가 제거되어 파편화된 메모리 영역을 앞에서부터 채워나가는 작업(Compaction)을 수행하게 된다.

 

- Mark 과정 : 먼저 Root Space로부터 그래프 순회를 통해 연결된 객체들을 찾아내어 각각 어떤 객체를 참조하고 있는지 찾아서 마킹한다.

- Sweep 과정 : 참조하고 있지 않은 객체 즉 Unreachable 객체들을 힙에서 제거한다.

- Compact 과정 : Sweep 후에 분산된 객체들을 힙의 시작 주소로 모아 메모리가 할당된 부분과 그렇지 않은 부분으로 압축한다.

 

이렇게 Mark and Sweep 방식을 사용하면 루트로부터 연결이 끊긴 순환 참조되는 객체들을 모두 지울 수 있다.

 

 

Minor GC의 동작 방식

 

객체가 새롭게 생성되면 Young 영역 중에서도 Eden 영역에 할당이 된다. 그리고 Eden 영역이 꽉 차면 Minor GC가 발생하게 되는데, 사용되지 않는 메모리는 해제되고 Eden 영역에 존재하는 객체는 (사용중인) Survivor 영역으로 옮겨지게 된다. Survivor 영역은 총 2개이지만 반드시 1개의 영역에만 데이터가 존재해야 한다.

 

1. 새로 생성된 객체가 Eden 영역에 할당된다.

 

2. 객체가 계속 생성되어 Eden 영역이 꽉차게 되고 Minor GC가 실행된다.

 

3. Mark 동작을 통해 reachable 객체를 탐색한다.

 

4. Eden 영역에서 살아남은 객체는 1개의 Survivor 영역으로 이동한다.

 

5. Eden 영역에서 사용되지 않는 객체(unreachable)의 메모리를 해제(sweep)

 

6. 살아남은 모든 객체들은 age 값이 1씩 증가  (*age 값 : Survivor 영역에서 객체가 살아남은 횟수를 의미하는 값)

 

7. 또다시 Eden 영역에 신규 객체들로 가득 차게 되면 다시한번 minor GC 발생하고 mark한다.

 

8. marking한 객체들은 비어있는 Survival 1로 이동하고 sweep

 

9. 다시 살아남은 모든 객체들은 age 1씩 증가

 

10. 이러한 과정을 반복

 

 

Major GC의 동작 방식

Old Generation은 길게 살아남는 메모리들이 존재하는 공간이다.

Old Generaction의 객체들은 거슬러 올라가면 처음에는 Young Generation에 의해 시작되었으나, GC과정 중에 제거되지 않은 경우 age 임계값이 차게되어 이동된 녀석들이다.

그리고 Major GC는 객체들이 계속 Promotion되어 Old 영역의 메모리가 부족해지면 발생하게 된다.

 

1. 객체의 age가 임계값(여기선 8로 설정)에 도달하게 되면  (*실제론 31정도 한다)

 

 

2. 이 객체들은 Old Generaction으로 이동된다. 이를 promotion이라 부른다.

 

3. 위의 과정이 반복되어 Old Generation 영역의 공간(메모리)이 부족하게 되면, Major GC가 발생하게 된다.

 

 

Major GC는 Old 영역이 데이터가 가득 차면 GC를 실행하는 단순한 방식이다.

Old 영역에 할당된 메모리가 허용치를 넘게 되면, Old 영역에 있는 모든 객체들을 검사하여 참조되지 않는 객체들을 한꺼번에 삭제하는 Major GC가 실행되게 된다.

 

하지만 Old Generation은 Young Generaction에 비해 상대적으로 큰 공간을 가지고 있어, 이 공간에서 메모리 상의 객체 제거에 많은 시간이 걸리게 된다.

Young Generaction은 크기가 작기 때문에 GC가 보통 0.5초~ 1초 사이에 끝나지만,

Old Generaction은 10배 이상의 시간을 사용한다.

 

바로 여기서 Stop The World 문제가 발생하게 된다.

Major GC가 일어나면 쓰레드가 멈추고 Mark and Sweep 작업을 해야 해서 CPU에 부하를 주기 때문에 멈추거나 버벅이는 현상이 일어나기 때문이다.

 

 

 

 

참고

https://inpa.tistory.com/entry/JAVA-%E2%98%95-%EA%B0%80%EB%B9%84%EC%A7%80-%EC%BB%AC%EB%A0%89%EC%85%98GC-%EB%8F%99%EC%9E%91-%EC%9B%90%EB%A6%AC-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%F0%9F%92%AF-%EC%B4%9D%EC%A0%95%EB%A6%AC

 

728x90