본문 바로가기
CS/JAVA

[JAVA] Garbage Collection / 가비지 컬렉션 이란?

by joossaem 2025. 7. 31.

첫 CS 관련 글로

자바의 Garbage Collection이 당첨되었다.

 

🧐 Garbage Collection 이란?

직역하면 '쓰레기 수거' 인데다,

dev 세계관에서 "쓰레기 == 쓸데없이 메모리를 잡아먹는 무언가" 까지는 문제없이 받아들여진다.

쓰레기(역할이 끝나버린 객체)를 청소부(Garbage Collector)가 주기적으로 청소하는 일련의 과정

을 바로 Garbage Collection(이하 GC) 이라고 하는 것이다.

 

이에 대해 GC가 언제 어떻게 왜 사용되는지를 중점적으로 살펴보겠다.


그 전에,

Garbage Collectoin을 이해하려면 힙에 대한 이해가 필수이다.

객체를 만들고 GC가 필요해지는 상황까지의 일련의 과정을 함께 해볼테니 마음을 단단히 먹어보자

 

목차

1. 힙 & 스택

2. 힙의 구조

3. 힙에서 일어나는 일

 

 

1️⃣ 힙 & 스택

자바의 JVM 메모리 구조는 총 다섯 가지 영역으로 구성되지만 [힙, 스택, 메서드영역, PC레지스터, 네이티브 메서드 스택]

GC를 이해하기 위해선 힙과 스택만 구분하면 된다.

아래 예제를 보자

public class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

public class Example {
    public static void main(String[] args) {
        int x = 10;		    		// (1)
        Person p1 = new Person("Alice", 20);   // (2)
        p1 = new Person("Bob", 25);		// (3)
    }
}

(1) - 스택 : 지역변수 x와 그 값 10이 저장됨

(2) - : new Person()을 통해 Person 객체 생성

      - 스택 : p1 이라는 참조 변수 생성 후 Person 객체의 주소 참조(저장)

(3)  - : 새로운 Person 객체 생성

       - 스택 : p1변수는 유지되지만 Alice의 주소가 아닌, Bob의 주소를 참조(저장)

 

여기서 포인트는 두 가지인데

1. 모든 객체는 실질적으로 힙 영역에 저장된다 

2. (3) 과정에서 Person(Alice) 객체는 어디에서도 쓰이지 못하는 쓰레기 객체가 되었다

정도가 되겠다.

 

최종적으로 Alice와 Bob의 상태를 비교해보자면

Bob은 p1이라는 변수가 참조하고 있고, Alice는 어떤 변수도 참조하고 있지 않아 접근이 불가능한 상태이다.

여기서 Bob과 같은 객체를 Reachable, Alice와 같은 객체를 Unreachable이라고 한다.

즉,  Unreachable 객체 == 쓰레기 객체가 되는 것이다.

 

 

 

 

2️⃣ 힙의 구조

이제 힙의 구조를 한번 살펴보자

크게 Young Generation 영역과 Old Generation 영역으로 나눌 수 있고

Young Generation 영역은 Eden 영역과 Survivor 영역으로 나뉜다.

 

객체가 생성되는 곳은 Eden 영역이라는 것만 기억하고 자세한건 이어서 살펴보자.

(크기가 너무 큰 객체는 처음부터 Old Generation에 할당되기도 한다.)

 

 

3️⃣ 힙에서 일어나는 일

1. 객체 생성으로 Eden 영역 꽉참

초록색 : Reachable / 회색 : Unreachable

프로그램이 실행됨에 따라 객체가 계속 생성되고 결국 그림과 같이 Eden 영역을 가득 채우게 된다.

이때 Eden을 가득 채운 객체들 중 일부는 쓰레기 객체이고 일부는 쓰레기가 아닌 객체일 것이다.

 

2. 작은형님 등장, Minor GC

힙의 Eden 영역이 가득 차면, 드디어 우리들의 Garbage Collector가 등장한다.

 

이때 GC에도 Minor, Magor, Full 등으로 급이 있다.

[Minor GC : Young Generation 담당 / Major GC : Old Generation 담당 / Full GC : 전체 담당]

 

Young Generation에서는 작은형님 급인 Minor GC가 담당하고,

Minor GC의 등장으로 쓰레기 객체는 모두 날라가고 그림과 같이 Reachable 객체들만 남게된다.

 

3. Stop-the-World (STW)

여기서 틈새로,

잠깐 짚고 넘어가야 할 현상이 바로 Stop-the-World 이다.

 

Stop-the-World 란,

Gargage Collection이 실행되는 동안 모든 애플리케이션 스레드를 일시적으로 정지시키는 현상이다.

(우리들의 작은 형님 Minor GC가 등장한 지금, 모든 스레드는 일시 정지 상태인것이다.)

 

가비지 컬렉션이 실행되고 전체 객체를 탐색하며 각 객체 상태를 확인하는 동안,

스레드가 객체 참조를 변경하면 GC의 판단이 틀어질 수 있기때문에

어떻게 보면 당연한 현상이고, 결국 이 시간을 0으로 만드는 것은 사실상 불가능하다.

 

따라서 모든 GC의 최종적인 목표는 

GC 작업과 애플리케이션 동작을 동시에 수행하며 STW 시간을 극도로 짧게 만들고

그 빈도수를 최소화 하는 것이다.

 

4. Micor GC 후 살아남은 객체 이동

어쨌든 Minor GC 사태 이후 살아남은 Reachable 객체들은 Survivor 영역으로 복사(이동)된다.

이때 객체들은 무조건 S0, S1 중 비어있는 곳으로 이동하는데, 첫 GC 사태인 만큼 당연히 둘 다 비어있을 것이다.

이때 가장 처음 이동할 때 어디로 이동시킬지는 내부적으로 지정이 가능하다고 하다.

하지만 중요한 점은 항상 비어있는 곳으로 간다는 점!!

 

여기서 추가로,

Minor GC 사태를 겪고 Survivor 영역으로 이동한 객체들은 살아남은 기념으로 Minor GC 사태 횟수를 기억하게 되고,

이 횟수를 age 값 이라고 한다. (실제로 Object Header에 기록됨) 

이 age값은 언제 사용되느냐?

Minor GC의 임계값과 비교를 위한 값이다. (임계값은 JVM에 저장되고 이 역시 설정 가능)

Minor GC 사태가 일어날 때마다 age값을 확인하고, 임계값에 도달한 객체들은 Old Generation으로 이동하게 되는데, 자세한 과정은 뒤에서 살펴보겠다.

 

5. 새로운 객체들로 다시 Eden 꽉차고 다음 Minor GC 발생, 객체들 이동

어쨌든 임계값에 도달한 객체가 나오기 전까지는 (=젊은 객체들만 있을 때는)

Eden 꽉참 -> Minor GC 발생 -> S0 또는 S1으로 이동(비어있는곳으로) -> 이동할때 각 객체 age값 +1

이 과정의 반복이다.

 

위 그림을 10초정도 쳐다보면 도움이 될 것 같다.

주목할 점은

 - S0 역역에 있던 객체들과 Eden 영역에서 살아남은 새로운 객체들 모두 비어있던 S1 영역으로 이동

 - S0 역역에 있던 객체들의 age=2, Eden 영역에서 살아남은 객체들의 age=1 로 각 1씩 증가

정도가 되겠다.

 

그림 이후 Minor GC 사태에서는 당연히 모든 객체가 S0 으로 넘어간다!

(S1 객체들 -> S0 | Eden 객체들 -> S0)

 

6. Age가 임계값을 넘어간 경우

위 과정의 반복으로 임계값에 도달한 객체가 생기면 드디어 Old Generation 영역을 사용할 때가 된거다.

 

위 그림은

지금까지 총 15번의 Minor GC를 겪었고, 임계값 또한 15라고 가정한 상황이다.

총 4개의 객체가 살아남았고 15살 객체2개, 14살객체 1개, 5살 객체 1개가 남았다.

임계값에 도달한 2개의 객체는 Old Generation 영역으로 이동하게 되고

그 뜻은, 이정도 살아남았으면 앞으로도 살 날이 많은 것 같으니 경로우대 해주겠다 라는 뜻이다.

이렇게 Young Gen 에서 Old Gen으로 넘어가는 과정을 Promotion 이라고 한다.

 

여기서 주목할 점은

Old Gen으로 넘어간 객체의 나이값이 사라진다는 것이다.

정확히는 사라진다라기보다는 무의미해진다고 표현하는 게 맞는 것 같은데,

이제 나이를 더 먹는다고 어디로 옯기거나 제거되지 않는다.

 

이후 과정은 또 Minor GC 사태의 반복이다.

언제까지?

Old Generation이 꽉 찰때까지.

 

7. 큰형님 등장, Major GC (Full GC)

수많은 Minor GC 사태, 그로인해 많은 Promotion 발생으로 Old Generation 영역이 꽉 찬 상황이다.

 

여기서 다시 한번

Minor GC는 Eden 영역이 꽉 찼을때 발생한다는 것을 다시 리마인드 해보자.

 

반면, Major GC는 지금처럼 Old Generation 영역이 꽉 찼을 때 일어나는 사태이다.

여기서 Major GC와 Full GC를 혼용하여 사용하기도 하지만 엄연히 다른 기술인 것을 알아야 한다.

 

Minor GC = Eden 담당

Major GC = Old Generation 담당

Full GC = 힙 전체 담당

 

그럼 왜 Major GC와 Full GC를 혼용해서 사용할까?

 

우선 각 GC별 발생 빈도와 걸리는 시간을 이해해야 한다.

Minor GC의 경우 탐색해야 하는 객체의 수가 물리적으로 적은 만큼 짧은 시간 안에 끝나고,

Major GC의 경우 탐색해야 하는 객체의 수가 물리적으로 많은 만큼 오랜 시간이 걸린다.

(실제 Eden 영역과 Old Generation 영역이 차지하는 비율은 약 1 : 3 정도이다.)

 

GC가 진행되는 동안은 어쨌든 전체 프로그램이 중단 되어야 하는데,

Major GC가 실행되는 긴 시간동안, Young Generation 영역은 놀고있을수만은 없는 노릇인거다.

따라서 Major GC를 '하는 김에' Minor GC를 같이 실행하는 경우가 대부분이라

Major와 Full을 혼용해서 사용하게 된 것이다.

 

어쨌든 이렇게 Major GC 사태까지,

Garbage Collection에 대한 전반적인 흐름을 모두 살펴보았다.

 

8.  조기 승진 (Premature Promotion)

일반적으로 진행되는 GC 실행 외에도 수많은 예외적인 상황이 분명 발생 할 것이다.

이중 조기 승진의 경우를 다뤄보자.

위 그림은 한바탕 Minor GC가 휘몰아 친 상황이다.

Young Generation 내의 살아남은 모든 객체는 아무도 임계값에 다다르지 않았고, 이제 모두가 S0 영역으로 이동하기면 된다.

하지만 사실 문제는 이미 발생했다.

바로 S1 영역이 이미 꽉 차있던 것이다.

S1 영역의 객체들이 고대로 S0으로 옮기기만 해도 S0이 가득 차게되는데,

여기에 새로 들어온 객체들도 Eden 영역에서 S0 영역으로 이사 대기중이다.

이때, 부족한 공간으로 인해 어쩔수 없이 몇몇 객체들이 Old Generation으로 이동하게 된다. (임계값이 채 되기도 않은 젊은 객체들이!!)

 

이러한 현상을 조기 승진 (Premature Promotion) 이라고 하고

Old Gen의 영역을 쓰는 만큼 Major GC의 시점을 앞당기는,

아주 안좋은 현상이라고 할 수 있다.

 

이를 방지하기 위해 Young Generation의 크기를 적절하게 설정하는 등의 조치를 취할 수 있겠다.

 

하지만 또 무작정 Young Generation의 크기를 늘려버리면

빈번하게 발생하는 Minor GC의 Stop-the-World의 시간이 길어지는 문제가 있으니

애플리케이션 특성에 맞는 적절한 균형을 찾는 게 중요한 과제인 것 같다.


✅ 결론

자바의 Garbage Collection의 간단한 개념과 흐름에 대해 정리해 보았는데,

더 많은 예외상황, 알고리즘 종류, 실제로 GC를 설정하는 법 등에 대해 추가로 더 알아보면 좋을 것 같다는 생각이 든다.

하지만 동시에 실무에서 이 GC를 직접적으로 열어봐야 할 상황이 현실적으로 많이 생길까? 하는 의문점이 드는 것 또한 사실이다.

 

어쨌든 GC의 장황한 개념보다는 그 동작 흐름에 대해 초점을 맞췄는데,

스스로도 많은 공부가 된 것 같아 아주 많이 상당히 뿌듯한 상태이다.😎

 

주기적으로 이런 주제에 대해 글을 작성해보는게 좋을 것 같다!

밀린 글이 이미 2개지만^^

 

 

끝!