기본 개념
- Java Platform의 핵심 기능
- 자동 메모리 관리
- 빠른 메모리 할당
- 효율적인 회수
- 다양한 사용 사례 제공
- 배치 처리
- UI application
- 작은 cloud service
GC는 왜 여러개가 있을까?
GC들마다 알고리즘적인 차이가 존재. 프로그램의 특성별로 GC 선택도 달라져야 할 것. (default는 G1 임)
[!note]
오라클 블로그에서 java 8 ~ java 18까지의 GC 발전과 여러 GC 차이점을 설명하고 있음
- JDK 18 에서 지원하는 GC 종류https://blogs.oracle.com/javamagazine/post/java-garbage-collectors-evolution
메모리 회수 (Memory Reclamation)
Liveness Determination
- 어떤 객체가 쓰레기(메모리가 회수가능한) 값인지 판단하는 과정
- JAVA의 GC는 Object Graph를 tracing(추적)하여 이 과정을 처리함
Reclaiming memory
메모리 회수 알고리즘에는 여러개가 존재함
자바의 GC 구조는 일반적으로 아래와 같음
→ 개발자는 G1 GC , Parallel GC , ZGC 같은 큰 범위인 GC 종류를 선택하면
GC가 메모리 회수 알고리즘을 뭘 선택할지는 JVM에 맡김
Mark-Sweep Algorithm
- 작동 원리
- Mark (표시)
현재 참조하고 있는 객체들을 표시 - Sweep (제거)
더 이상 표시(참조)되지 않는 객체를 제거하여 메모리를 회수함
- Mark (표시)
이때, 참조하고 있는 객체들을 파악하기 위해서 필요한것이 Object Graph임
이를 통해 객체간의 관계를 추적해서 이 알고리즘을 처리할 수 있음
- 장점
- 구현이 간단함
- GC 처리동안 메모리간의 이동이 필요 없음
- 단점
- Memory fragmentation(메모리 단편화)가 발생함

Mark-Compact Algorithm
- 작동원리
Mark-Sweep알고리즘을 그대로 사용하되 해당 알고리즘이 끝나면 빈 메모리를 한쪽으로 compact(압축)함
- 장점
- Mark-Sweep 알고리즘을 그대로 차용하되, 메모리 단편화를 방지할 수 있음
- 단점
- GC 처리동안 메모리간의 이동이 발생함

Copying Algorithm
- 작동원리
- 메모리를 두 군데 (From, To)로 나누고 한쪽에서만 객체를 생성하다가 GC가 발생하면 살아있는 객체만 From에서 To로 복사하고 From은 비움
- 장점
- memory fragmentation이 없음
- 복사만 하므로 빠름
- 단점
- 메모리를 from, to 두군데를 사용하므로 메모리 공간 낭비임
- 큰 heap에서는 비효율적임
Copying Algorithm은 전체 heap 메모리를 반으로 나눠서 From, To 로 나눠버림
따라서 예를 들면 8GB라면 4GB만 사용할 수 있는 진짜 메모리(From)임
GC 작동 시점
Stop-the-world
모든 자바 스레드를 멈추고 GC를 실행

Concurrent collection
자바 스레드 작업과 동시에 같이 GC를 실행

Generation Collection
효율적인 GC를 만들기 위한 기술
Java 팀에서는 “Generation hypothesis (세대 가설: 대부분의 객체는 짧은 기간동안만 사용된다는 가설)”을 기반으로 GC를 효율적으로 만들기 위해 노력중
heap memory를 Young Generation과 Old Generation 으로 분리하여 새로 만들어진 객체는 Young Generation에 배치한 후 관리함. 이때 Young Generation 대상만 GC를 자주 실행함
GC 선택 기준
Throughput (처리량)
일정 시간 당 처리한 트랜잭션 수
Latency (지연시간)
한 트랜잭션동안 걸린 시간
Footprint (자원)
GC 로 인해 사용되는 CPU 및 메모리 자원
[!tip]
이 중 하나를 중점적으로 택하고 그에 맞는 GC를 선택하는게 맞다.
GC 종류
| GC | Focus area | Support |
|---|---|---|
| Serial | Memory footprint (메모리 자원 중시) | OpenJDK, Oracle |
| Parallel | Throughput (처리량 중시) | OpenJDK, Oracle |
| G1 | Balanced performance (균형잡힌 성능) | OpenJDK, Oracle |
| ZGC | Low latency (낮은 지연속도) | OpenJDK, Oracle |
| Shenandoah | Low latency (낮은 지연속도) | OpenJDK |
- Serial GC
구현방식이 단순하고 GC 가 필요한 추가적인 메모리 사용량이 낮으므로 메모리 오버헤드가 낮음.
Java 어플리케이션 스레드를 중단하고 GC를 단일 스레드로 수행하므로 큰 heap memory일 경우 최적의 방법이 아닐수는 있지만 작은 단위의 서비스 경우 괜찮은 대안일 수 있음.

- Parallel
Serial GC와 거의 비슷하지만 GC를 멀티쓰레드를 사용해 병렬처리로 수행함
JDK 8까지 default GC 였음

- G1
Throughput + Latency 의 균형을 잡은 GC
Stop-the-world + Concurrent GC 혼합된 방식- 작동방식
- Old Generation의 생존 여부를 Java Appliction과 동시에 Marking 하고
Mixed GC(Young + Old Generation 일부 를 같이 수집하는 방식) 를 여러번에 거쳐 수행함
JDK9 ~ 부터 default GC 임 - 작동방식

- Object Graph
객체들간의 관계를 파악하기 위해 만들어놓은 객체 탐색(DFS Depth-First Search: 아래로 내려가며 찾는 그래프 탐색 알고리즘)을 통해 만들어진 그래프
Root Object → 객체 → 객체 → … 형식으로 되어있음. Root Object 로부터 참조된 객체,요소들을 따라감. 이때 참조가 끊어진 객체들을 garbage로 취급하게 된다
- G1 GC 의 메모리 회수 전략
- 전체 heap memory를 region 단위로 나누고,
- young generation은 Copying 회수 방식 채택,
- old generation은 Mark-Compact 방식 채택함
지금 heap에 여유가 있나?→이 region이 얼마나 오래되었는가?와 같은 흐름을 따름
- Generation
Heap 메모리에 영역을 나누어 객체들을 관리함
GC 성능을 높이기 위해 전략적으로 나눈 “영역” → “최적화”
새로운 객체 및 짧은 생명의 객체 → Young → “짧은 주기로 빠르게 GC”
오래된 객체 및 긴 생명의 객체 → Old → “긴 주기로 덜 자주 느리게 GC”
- Young Generation (= Minor Generation )
- 새로 객체가 생성되면 여기로 저장됨
- 짧은 생명주기를 가짐 → GC 대상 1순위
- GC가 자주 & 빠르게 일어남
- 내부적으로 Eden, Survivor 로 분리되어있는데 객체가 처음 생성되면 Eden, 여기서 살아남은 객체들이 Survivor로 이동 (잠시 거쳐가는 공간)
- Old Generation (= Major Generation = Full Generation)
- Young Generation에서 살아남은 객체가 이쪽으로 이동됨
- 오래 살아남은 객체가 이쪽으로 이동되므로 메모리 회수빈도가 낮음
- GC가 낮은 빈도 & 느리게 일어남
- Young Generation (= Minor Generation )
- Memory Fragmentation (메모리 단편화)
메모리 중간중간을 회수하면 메모리가 띄엄띄엄 공간이 생김. 연속된 공간 확보가 안됨.
예를 들면
[O][O][O][O][O][O] → 2번, 5번 메모리 회수할 경우 → [O][ ][O][O][ ][O] → 중간에 빈 공간이 생김
- G1 에서는 옵션을 통해 pause 목표시간을 설정가능함
-XX:MaxGCPauseMillis=100
'JAVA' 카테고리의 다른 글
| 제어의 역전과 의존성 주입 (2) | 2025.07.24 |
|---|---|
| Spring Security 개념 정리 (0) | 2025.07.21 |