-
GC, 넌 누구야....카테고리 없음 2025. 6. 17. 19:51
오늘은 평소에 생각하지 않고 넘겼었던 GC(가비지 컬렉터)에 대해서 알아보도록 하겠다.
1. GC가 뭐야?
GC는 메모리 관리 기법 중 하나이고, 동적으로 할당했던 메모리 영역 중에서 필요 없게 된 영역을 알아서 해제해 주는 기법이다.* 동적으로 할당했던 메모리 영역은 Heap 영역. -> 여기서 힙 영역은 애플리케이션 실행 중에 생성되는 객체 인스턴스를 저장하는 영역이다.
* 필요 없게 된 영역은 어떤 변수도 가리키지 않게 된 영역을 의미.C나 C++ 같은 경우는 Heap 영역의 메모리를 관리하기 위해 코드 레벨에서 할당받고 해제를 해주어야 했다. 또한 할당받은 메모리 영역을 제대로 해제하지 않아 Memory Leak이 발생하기도 하였다.
GC를 도입한다면 수동으로 메모리를 관리하던 것에 대한 에러를 해결할 수 있다..
개발자의 실수로 인한 메모리 누수를 막을 수 있고, 해제된 메모리에 접근하는 오류와 해제된 메모리를 또 해제하는 이중 해제 또한 막을 수 있다.
하지만 어떤 메모리 영역이 해제의 대상이 될지 검사하고 해제하는 일은 우리의 프로그램이 해야 하는 일을 방해하는 요소가 될 수도 있다.
특히 실시간 통신이 강조되는 프로그램일 경우에는 GC의 사용이 부적절할 수 있다.
2. GC는 알겠는데, 해제할 메모리 영역들을 어떻게 판단함?
GC 알고리즘에 대해서 설명하기 전에 Root Space에 대해서는 간단하게 스택 변수, 전역 변수 등 heap 영역 참조를 담은 변수라고 생각하면 편하다.
2.1) Reference Counting 알고리즘

Reference Count라는 별도의 숫자를 가지고 있습니다. 여기서 Reference Count는 몇 가지 방법으로 해당 객체에 접근할 수 있는지를 뜻합니다.
해당 객체에 접근할 수 있는 방법이 하나도 없다면.. 즉, Reference Count가 0이 다다르면 GC의 대상이 되는 것입니다.
하지만 Reference Counting 알고리즘은 순환참조의 한계점이 존재합니다.
2.2) Mark and Sweep 알고리즘

Mark and Sweep 알고리즘은 Reference Counting의 순환참조 문제를 해결할 수 있다.
해당 방식은 루트에서부터 해당 객체에 접근 가능한지를 해제의 기준으로 생각하고 루트부터 그래프 순회를 통해 연결된 객체를 찾는다.(이것이 마크임)
연결이 끊어진 객체들은 지우는 방식입니다.(이것이 Sweep)
* 루트로부터 연결된 객체는 Reachable
* 연결되지 않았다면 Unreachable
그림에서는 Sweep 이후에 분산되어 있던 메모리가 깔끔하게 정돈되어 있는데 이를 메모리 파편화를 막는 Compaction이라고 한다. 다만 Compaction은 필수가 아니다.
이와 같이 Mark and Sweep 기법을 사용하면 루트로부터 연결이 끊긴 순화참조되는 객체들도 모두 지울 수 있다.
간단하게 Mark and Sweep의 특징은 의도적으로 GC를 실행시켜야 하고, 애플리케이션 실행과 GC 실행이 병행된다.
3. JVM의 GC?? Heap 영역? - 의도적으로 GC를 실행시켜야 한다.

크게 Young Generation과 Old Generation으로 나뉜다.
* Young Generation에서 발생하는 GC는 Minor GC라고 부른다.
* Old Generation에서 발생하는 GC는 Major GC라고 부른다.
Young Generation은 또 세 영역으로 나뉘게 된다. -> Eden, Survival 0, Survival 1 영역으로 나뉨.
* Eden은 새롭게 생성된 객체들이 할당되는 영역임.
* Survival 영역은 Minor GC로부터 살아남은 객체들이 존재하는 영역임. -> Survival 영역에는 특별한 규칙이 하나 있는데 0 혹은 1 둘 중 하나는 꼭 비어있어야 한다.
age-bit는 Minor GC에서 살아남을 때마다 1씩 증가합니다.
Eden에 새롭게 생성된 객체들이 전부 채워지면 Minor GC 발생 -> Survival로 이동 -> 또다시 Eden에 새롭게 생성된 객체들이 전부 채워지면 다시 Minor GC 발생 -> Survival로 이동(age-bit 증가) -> Old Generation으로 이동(Promotion) -> Old Generation 전부 채워짐 -> Major GC 발생(Major 시간 > Minor 시간)
4. GC 실행 방식 - 애플리케이션 실행과 GC 실행이 병행된다.
먼저 어떤 방식들로 애플리케이션 실행과 GC의 실행되는지 알아보기 전에 Stop The World라는 개념을 알고 가야 한다.
Stop The World는 GC를 실행하기 위해 JVM이 애플리케이션 실행을 멈추는 것이다.
4.1) Serial GC
Serial GC는 단일 스레드를 활용하여 GC 작업을 처리하는 방식이다.
Stop The World
Serial GC는 애플리케이션 스레드가 많아도 GC는 단 하나의 스레드로만 처리되며, GC 도중 애플리케이션은 멈춰 있다.
장점
구현이 단순하고 안정적이다.
CPU가 적고 스레드가 적은 환경에 적합하다.
작은 힙 사이즈에는 오히려 빠르게 동작할 수 있다.
단점
멀티 스레드 환경에서는 비효율적이다.
GC 도중 전체 애플리케이션이 중단되기 때문에 지연시간이 길다.
대규모 서버나 실시간성이 중요한 시스템에는 부적합하다.
4.2) Parallel GC
Parallel GC를 여러 개의 스레드로 GC 작업을 처리하는 방식이다.
Stop The World
GC 자체가 병렬로 처리되어 빨리 끝나기 때문에 애플리케이션의 일시정지는 짧아집니다.
장점
멀티 코어 환경에서 뛰어난 GC 처리 성능을 보여준다.
GC 시간이 짧고 효율적이다.
단점
여전히 Stop The World가 발생한다.
응답 지연 시간에 민감한 프로그램에서는 부적합하다.
GC 스레드가 많아지면 GC 자체는 빨라지지만, CPU 리소스 경쟁이 생길 수 있다.
4.3) G1 GC
G1 GC는 지연시간을 예측 가능하게 하면서도 전체 처리량도 확보할 수 있도록 설계된 GC이다.
Stop The World
G1GC도 Stop The World가 발생하기는 하지만 전체 힙을 수거하지 않고 일부 Region만 수거하고, GC 작업도 병렬처리를 하기 때문에 애플리케이션의 중단 시간이 짧고 예측 가능하다.
장점
지연 시간과 처리량을 동시에 고려한다.
Heap이 클수록 효율적이다.
Stop The World 일지라도 일부만 발생하기 때문에 예측 가능하다.
Region 기반 구조로 인해 단편화가 줄어든다.
단점
GC 로직이 복잡해서 추가적인 메모리/CPU 오버헤드 존재한다.
초소형 애플리케이션에서는 오히려 성능이 저하될 수 있다.
매우 짧은 응답 지연이 필요한 실시간 시스템에는 부적합하다.
평소에 알고만 있고 잘 몰랐었던 GC의 동작 원리 종류들에 대해서 알아볼 수 있는 기회가 되어 좋았다!