728x90
반응형
Garbage Collection 개념
- 가비지 컬렉션(Garbage Collection, GC): 자바의 메모리 관리 방법 중의 하나로 JVM(자바 가상 머신)의 Heap 영역에서 동적으로 할당했던 메모리 중 필요 없게 된 메모리 객체(garbage)를 모아 주기적으로 제거하는 프로세스
- 가비지 컬렉터가 없어 수동으로 메모리 할당과 해제를 해주어야 하는 C/C++과 달리, Java는 가비지 컬렉터가 메모리 관리를 대신 수행해줌
- => 메모리 누수(Memory Leak) 문제에서 대해 관리없이 개발에만 집중 가능
- 가비지 컬렉션(GC)이 한번쓰이고 버려지는 객체들을 주기적으로 비워줌 => 한정된 메모리를 효율적으로 사용 가능
- 단점
- 메모리가 언제 해제되는지 정확하게 알 수 없어 제어하기 힘듦
- Stop-The-World(STW): 가비지 컬렉션(GC)이 동작하는 동안에는 JVM이 동작을 멈추는 현상 => 오버헤드 발생
- GC가 작동하는 동안 GC 관련 Thread를 제외한 모든 Thread가 멈추게 됨
- STW 시간을 최소화하는 것이 중요 = GC 튜닝
과거 인터넷 익스프롤러는 GC가 너무 빈번하게 실행되어 성능 이슈가 있었음
GC 대상
GC 대상 여부는 도달성(Reachability)라는 개념을 적용함
- Reachable: 객체가 참조되고 있는 상태로, 객체에 레퍼런스가 있음
- Unreachable: 객체가 참조되고 있지 않은 상태로, 객체에 유효한 레퍼런스가 없음 = GC 대상
- JVM 메모리에서는 객체들은 실질적으로 Heap영역에서 생성되고 Method Area이나 Stack Area 에서는 Heap Area에 생성된 객체의 주소만 참조하는 형식으로 구성됨.
- 하지만, Heap Area 객체의 메모리 주소를 가지고 있는 참조 변수가 삭제될 수 있음 (by 메서드 종료 등의 특정 이벤트)
- => Heap영역에서 어디서든 참조하고 있지 않은 객체(Unreachable)들이 발생
- => 이러한 객체들을 주기적으로 가비지 컬렉터가 제거하는 것
GC 청소 방식 - Mark And Sweep
- 표시-정리 알고리즘(Mark-Sweep): 다양한 GC에서 사용되는 객체를 솎아내는 내부 알고리즘 (가비지 컬렉션이 동작하는 아주 기초적인 청소 과정)
- Mark 과정: 참조되는 모든 객체를 표시하는 과정으로, Root Space로부터 그래프 순회를 통해 연결된 객체들을 찾아내어 각각 어떤 객체를 참조하고 있는지 찾아서 마킹
- Root Space: Heap 메모리 영역을 참조하는 method area, static 변수, stack, native method stack
- 즉, Method Area의 static 변수, Stack의 로컬 변수, Native Method Stack의 JNI 참조 등
- Root Space: Heap 메모리 영역을 참조하는 method area, static 변수, stack, native method stack
- Sweep 과정 : 참조하고 있지 않은 객체(마킹되지 않은 객쳬) 즉 Unreachable 객체들을 Heap에서 제거
- 메모리 단편화 문제: Sweep 과정으로 삭제된 객체의 메모리 공간이 불연속적일 수 있음..
- => 크기가 큰 객체를 할당할 때 메모리 할당에 어려움이 있을수 있음 => Compact 과정을 거쳐 해결
- 메모리 단편화 문제: Sweep 과정으로 삭제된 객체의 메모리 공간이 불연속적일 수 있음..
- Compact 과정 : Sweep 후에 분산된 객체들을 Heap의 시작 주소로 모아 메모리가 할당된 부분과 그렇지 않은 부분으로 압축
- 가비지 컬렉터 종류에 따라 하지 않는 경우도 있음
- Mark 과정: 참조되는 모든 객체를 표시하는 과정으로, Root Space로부터 그래프 순회를 통해 연결된 객체들을 찾아내어 각각 어떤 객체를 참조하고 있는지 찾아서 마킹
- Mark-Sweep 방식을 통해 루트로부터 연결이 끊긴 순환 참조되는 객체들을 모두 지울 수 있음
GC 동작 과정
세대별 가비지 컬렉션(Generation Garbage Collection)
- JVM의 힙(heap) 영역은 동적으로 레퍼런스 데이터가 저장되는 공간으로, GC 대상이 되는 공간
- 객체는 대부분 일회성되며, 메모리에 오랫동안 남아있는 경우는 드물다는 것을 전제로 설계되었음 (
Weak Generational Hypothesis
) - 이러한 특성을 이용해 JVM 개발자들은 보다 효율적인 메모리 관리를 위해, 객체의 생존 기간에 따라 물리적인 Heap 영역을 나누게 되었음
- 객체는 대부분 일회성되며, 메모리에 오랫동안 남아있는 경우는 드물다는 것을 전제로 설계되었음 (
- 세대별 가비지 컬렉션(Generation Garbage Collection)
- Young 영역(Young Generation)
- 새롭게 생성된 객체가 할당(Allocation)되는 영역
- 대부분의 객체가 금방 Unreachable 상태가 되기 때문에, 많은 객체가 Young 영역에 생성되었다가 사라짐
Minor GC
: Young 영역에 대한 가비지 컬렉션(Garbage Collection)
- Old 영역(Old Generation)
- Young영역에서 Reachable 상태를 유지하여 살아남은 객체가 복사되는 영역
- Young 영역보다 크게 할당되며, 영역의 크기가 큰 만큼 가비지는 적게 발생
Major GC
(또는Full GC
): Old 영역에 대한 가비지 컬렉션(Garbage Collection)
- Young 영역(Young Generation)
- Old 영역이 Young 영역보다 크게 할당되는 이유
- Young 영역의 수명이 짧은 객체들은 큰 공간을 필요로 하지 않고, 큰 객체들은 Young 영역이 아니라 바로 Old 영역에 할당되기 때문
- Young 영역은 더 효율적인 GC를 위해 다시 3가지 영역(Eden, Survival 0, Survival 1)으로 나뉨
- Eden
- new를 통해 새로 생성된 객체가 위치
- 정기적인 garbage 수집 후 살아남은 객체들은 Survivor 영역으로 보냄
- Survival 0 / Survival 1
- 최소 1번의 GC 이상 살아남은 객체가 존재하는 영역
- Survivor 영역의 제한 조건으로 Survivor 영역 중 반드시 1개는 사용되어야 하고, 나머지는 비어 있어야 함
- 두 Survivor 영역에 모두 데이터가 존재하거나, 모두 사용량이 0이라면 현재 시스템이 정상적인 상황이 아니라는 것
- Eden
Minor GC
- 모든 객체는 처음에는 Young Generation에 생성
- Young Generation의 공간은 Old Generation에 비해 상대적으로 작기 때문에 메모리 상의 객체를 찾아 제거하는데 적은 시간이 걸림
- 과정
- Eden 영역에 처음 생성된 객체 위치
- Eden 영역이 꽉차게 되고 Minor GC가 실행
- Mark 동작을 통해 reachable 객체를 탐색
- Eden 영역에서 살아남은 객체는 1개의 Survivor 영역으로 이동
- Eden 영역에서 사용되지 않는 객체(unreachable)의 메모리를 해제(sweep)
- 살아남은 모든 객체들은 age값이 1씩 증가
- age 값: Survivor 영역에서 객체의 객체가 살아남은 횟수를 의미하는 값
- age 값이 임계값에 다다르면 Promotion(Old 영역으로 이동) 여부를 결정
- 이 age의 기본 임계값은 31
- 다시 Eden 영역에 신규 객체들로 가득 차게 되면 다시한번 Minor GC 발생하고 mark
- 이때, Survivor 0에 있는 객체들도 marking 대상임
- marking 한 객체들을 비어있는 Survival 1으로 이동하고 sweep
- 다시 살아남은 모든 객체들은 age가 1씩 증가
- 1 ~ 9 과정을 반복
Major GC(= Full GC)
- Old Generation의 객체들은 거슬러 올라가면 처음에는 Young Generation에 의해 시작되었으나, GC 과정 중에 제거되지 않은 경우 age 임계값이 차게되어 이동된 녀석들
- Major GC는 객체들이 계속 Promotion되어 Old 영역의 메모리가 부족해지면 발생
- Old 영역의 데이터가 가득 차면 GC를 실행하는 단순한 방식
- 과정
- Survivor 영역에 있는 객체의 age가 임계값(여기선 8로 설정)에 도달하게 되면, 이 객체들은 Old Generation 으로 이동 = Promotion 과정
- Promotion이 반복되어 Old Generation 영역의 공간(메모리)가 부족하게 되면 Major GC가 발생
- Old 영역에 있는 모든 객체들을 검사하여 참조되지 않는 객체들을 한꺼번에 삭제
- Old Generation은 Young Generation에 비해 상대적으로 큰 공간을 가지고 있어, 이 공간에서 메모리 상의 객체 제거에 많은 시간이 걸림
- Minor GC: 0.5 ~ 1초
- Major GC: Minor GC의 10배 이상의 시간 => Stop-The-World 문제 발생
- STW 문제를 최소화하기 위해 다양한 GC 알고리즘들이 등장
GC 알고리즘
여기서 소개할 GC 알고리즘은 모두 설정을 통해 Java에 적용할수 있다. 즉, 상황에 따라 필요한 GC 방식을 설정해서 사용할 수 있다!
Serial GC
- 서버의 CPU 코어가 1개일 때 사용하기 위해 개발된 가장 단순한 GC
- 싱글 스레드이기에 가장 stop-the-world 시간이 길다
- Minor GC 에는 Mark-Sweep을 사용, Major GC에는 Mark-Sweep-Compact를 사용
- 실무에서 사용하는 경우는 없다(CPU 코어가 1개인 경우에만 사용)
Serial GC 실행 명령어
java -XX:+UseSerialGC -jar Application.java
Parallel GC
- Java 8의 기본 GC 알고리즘
- Serial GC와 기본적인 알고리즘은 같지만, Young 영역의 Minor GC를 멀티 쓰레드로 수행 (Old 영역은 여전히 싱글 쓰레드)
- => Serial GC에 비해 stop-the-world 시간 감소
Parallel GC 실행 명령어
java -XX:+UseParallelGC -jar Application.java
# -XX:ParallelGCThreads=N : 사용할 쓰레드의 갯수
Parallel Old GC(Parallel Compacting Collector)
- Parallel GC를 개선한 버전
- Young 영역 뿐만 아니라, Old 영역에서도 멀티 쓰레드로 GC 수행
- 새로운 가비지 컬렉션 청소 방식인 Mark-Summary-Compact 방식을 이용
Parallel Old GC 실행 명령어
java -XX:+UseParallelOldGC -jar Application.java
# -XX:ParallelGCThreads=N : 사용할 쓰레드의 갯수
CMS GC(Concurrent Mark Sweep)
- 애플리케이션의 쓰레드와 GC 쓰레드가 동시에 실행되어 stop-the-world 시간을 최대한 줄이기 위해 고안된 GC
- 원리: Reachable 한 객체를 한번에 찾지 않고 나눠서 찾는 방식 사용 (4 STEP)
- Initial Mark
- Concurrent Mark
- Remark
- Concurrent Sweep
- 장점: Stop-the-World 시간 감소
- 단점
- GC 대상 파악 과정이 매우 복잡함
- Compact 과정이 없어서 메모리 파편화가 발생할 수 있고, 처리량이 증가할 때 예측할 수 없는 STW가 발생 가능
- Java 14에서 사용이 중지
CMS GC 실행 명령어
# deprecated in java9 and finally dropped in java14
java -XX:+UseConcMarkSweepGC -jar Application.java
G1 GC(Garbage First)
- CMS GC를 대체하기 위해 jdk 7 버전에서 최초로 release된 GC
- 원리
- 힙을 Young / Old 영역이 아닌 완전히 다르고 더 작은 영역 개념인 Regoin으로 나눔
- 전체 Heap 영역을 Region이라는 영역으로 체스판같이 분할 후, 상황에 따라 Eden, Survivor, Old 등 역할을 고정이 아닌 동적으로 부여
- Eden, Survivor, Old 역할에 더해 Humongous와 Available/Unused라는 2가지 역할을 추가
- Humongous: Region 크기의 50%를 초과하는 객체를 저장하는 Region
- Available/Unused: 사용되지 않는 Region
- 전체 Heap에 대해 탐색하지 않고 부분적으로 Region 단위로 탐색하여, garbage가 많은 Region에만 우선적으로 GC 수행
- GC가 돌때마다 살아남으면 Eden → Survivor0 → Survivor1으로 순차적으로 이동하는 것이 아니라, 더욱 효율적이라고 생각하는 위치로 객체를 Reallocate(재할당)
- 이러한 작업은 concurrent하게 수행되어 애플리케이션의 지연도 최소화 가능
- 장점
- Stop-the-World 시간을 예측 가능하게 줄이고, 힙을 더 세분화하여 효율적으로 가비지 컬렉션을 수행
- 어떠한 GC 방식보다 처리 속도가 빠름
- 단점: 몇몇 특정 상황에서 처리량이 감소
- Java 9+ 버전의 디폴트 GC
- 4GB 이상의 힙 메모리, Stop the World 시간이 0.5초 정도 필요한 상황에 사용 (Heap이 너무작을경우 미사용 권장)
G1 GC 실행 명령어
java -XX:+UseG1GC -jar Application.java
Shenandoah GC
- 강력한 Concurrency와 가벼운 GC 로직 -> heap 사이즈에 영향을 받지 않고 일정한 pause 시간이 소요가 특징
- 기존 CMS가 가진 단편화, G1이 가진 pause의 이슈를 해결
- 장점: 일시 중지 시간을 매우 짧게 유지하면서도 전반적인 처리량을 유지 가능
- 단점: 일부 애플리케이션에 대한 최적화가 필요할 수 있으며, 모든 플랫폼에서 지원되지 않을 수 있음
- Java 12에 릴리즈
Shenandoah GC 실행 명령어
java -XX:+UseShenandoahGC -jar Application.java
ZGC(Z Garbage Collector)
- 대량의 메모리(8MB ~ 16TB)를 low-latency로 잘 처리하기 위해 디자인 된 GC
- 원리: G1의 Region 처럼, ZGC는 ZPage라는 영역을 사용하며, G1의 Region은 크기가 고정인데 비해, ZPage는 2mb 배수로 동적으로 운영
- 장점: 힙 크기가 증가하더라도 'stop-the-world'의 시간이 10ms를 넘지 않음
- 단점: 일부 응용 프로그램에서는 ZGC가 다른 가비지 컬렉터보다 성능이 떨어질 수도 있고 메모리 사용량이 더 클 수 있음
- 일반적으로는 성능 및 일시 중지 시간 측면에서 우수한 결과를 보이는 편
- Java 15에 릴리즈
ZGC 실행 명령어
java -XX:+UnlockExperimentalVMOptions -XX:+UseZGC -jar Application.java
728x90
반응형
'Java' 카테고리의 다른 글
[Java] 예외 처리 (2) | 2025.01.03 |
---|---|
[Java] 자바 기본 개념 정리 (기본 문법 제외) (2) | 2025.01.02 |
[Java] JVM / JRE / JDK (3) | 2025.01.01 |
[Java] Annotation (0) | 2024.10.10 |
[Java] Reflection (0) | 2024.10.10 |