Garbage Collector
: 메모리 관리를 개발자가 직접 명시적으로 수행하지 않고, JVM에서 자동으로 수행해주는데 이와 같은 역할을 수행하는 프로세스
JAVA의 GC 도입의 전재 가설
- 대부분의 객체는 금방 접근 불가능 상태가 됨
- 오래된 객체에서 젊은 객체로의 참조는 아주 적게 존재
- 이러한 전재 가설 때문에 Young 영역과 Old영역으로 나누어짐
- 객체를 생성하고 다른 메소드나 클래스에 해당 객체를 전달한 후에는 다시 참조하지 않는 경우가 대부분
- 오래된 객체에서 젊은 객체로의 참조는 아주 적게 발생
- GC가 발생하지 않으면 현재 참조되지 않는 객체들이 메모리를 점유하고 있음
GC의 역할
- 메모리 할당
- 사용 중인 메모리 인식
- 사용하지 않는 메모리 인식
JVM 메모리 영역
- 클래스 영역 / 자바 스택 영역 / 힙 영역 / 네이티브 스택 영역
- GC는 JVM의 메모리 영역 중 힙 영역에서 이루어짐
-> JAVA에서 새로운 객체가 할당될 때, Heap 메모리 영역에 할당되기 때문
1. Heap 영역 구분
- New / Young 영역 : 생성된지 얼마 안된 객체를 저장, 시간이 지남에 따라 우선순위가낮아지면 Old영역으로 옮겨짐
- Old 영역 : 생성된지 오래된 객체를 저장
- Perm 영역 : Class, Method 등의 코드가 저장되는 영역, 자바 언어 레벨에서 사용되지 않음
2. Minor GC
: New / Young 영역의 GC
- 이에 사용되는 알고리즘은 Copy & Scavenge라고 칭함
- New / Young 영역은 Eden / Survivor 영역으로 나뉘어짐
1) Eden
- 자바 객체가 생성되지마자 저장되는 곳
- 객체가 계속 생성되어 Eden 영역이 다 차게 되면, Minor GC가 발생 -> 살아남은 객체는 Survivor 영역 중 하나로 이동
2) Survivor
- Survivor1 / Survivor2로 나누어짐 -> 우선순위가 존재하는 건 아님
- 한 Survivor 영역이 가득 차게 되면 그 중 살아남은 객체는 다른 Survivor 영역으로 이동
- 가득한 Survivor 영역은 아무 데이터가 없는 상태가 됨
- Survivor 영역 중 하나는 반드시 비어있는 상태여야 함 -> 아니라면 시스템이 비정상적인 상황
- 더 큰 객체가 생성되거나, 더 이상 Young 영역에 공간이 남지 않으면 Survivor 영역에서 오래된 객체가 Old 영역으로 이동 -> 이를 Promotion이라고 칭함
3. Object Aging
: Survivor영역에서 Survivor 영역으로 이동할 때 마다 객체의 살아남은 횟수를 나타내는 age 값을 증가시킴
- Object Header에 기록하며, age를 보고 Minor GC때 Old영역으로 이동을 결정
- 속도가 매우 빠르고, 작은 크기의 메모리를 콜렉팅하는 데에 효과적
- 자주 일어나기 때문에 시간이 짧은 알고리즘이 적합
4. Major GC(Full GC)
: Old 영역의 GC
- 이에 사용되는 알고리즘을 Mark & Compact라고 칭함
- Minor GC 과정에서 삭제되지 않고, Old Generation 영역으로 옮겨진 객체 중 미사용된다고 판단되는 객체를 메모리에서 삭제
- 작동 과정 )
- 객체들의 참조를 확인하면서 참조가 연결되지 않은 객체를 표시
- 작업이 끝나면 사용되지 않는 객체를 모두 표시하고 이 표시된 객체들을 삭제
- Full GC는 속도가 매우 느림
- Full GC가 일어나는 도중에는 순간적으로 자바 애플리케이션이 멈춰버리기 때문에 Full GC가 일어나는 정도와 시간은 애플리케이션의 성능과 안정성에 아주 큰 영향을 미침
GC 발생 시나리오
1. 객체가 생성되면 Eden 영역에 위치
2. Eden 영역이 가득 차게 되면 Minor GC가 발생하여 참조가 없는 객체는 삭제되고, 참조 중인 객체는 Survivor 영역으로 이동 -> 참조가 되고 있는 객체 : reachable
3. Survivor 영역이 가득 차게 되면, Minor GC가 발생하여 참조가 없는 객체는 삭제되고, 참조 중인 객체는 다른 Survivor 영역으로 이동
4. Survivor 영역에서 GC 과정을 반복하며, 계속 참조 중인 객체는 Old 영역으로 이동
5. Eden 영역에서 Survivor 영역으로 이동할 때 객체가 남아있는 영역보다 클 경우 Old 영역으로 이동
GC의 동작 방식
- Young 영역과 Old 영역은 서로 다른 메모리 구조로 되어있기 때문에, 세부적인 동작 방식은 다름
- 기본적으로 GC가 수행되면 2가지의 공통적인 단계를 따름
1. Stop The World
: GC 실행을 위해 JVM이 애플리케이션의 실행을 멈추는 작업
- GC가 실행될 때는 GC를 실행하는 스레드를 제외한 모든 스레드들의 작업이 중단되고, GC가 완료되면 작업이 재개됨
- 모든 스레드의 작업이 중단되면 애플리케이션이 멈추기 때문에, GC의 성능 개선을 위해 튜닝을 한다고 하면 보통 Stop The World의 시간을 줄이는 작업을 함
- JVM에서도 이러한 문제를 해결하기 위해 다양한 실행 옵션을 제공하고 있음
2. Mark And Sweep
- Mark
: 사용되는 메모리와 사용되지 않는 메모리를 식별하는 작업
- Sweep
: Mark 단계에서 사용되지 않음으로 식별된 메모리를 해제하는 작업
- 동작 원리 )
- 참조되는 변수를 구별
- 참조되지 않는 변수를 해제
- Stop The World를 통해 모든 작업이 중단되면, GC는 스택의 모든 변수 또는 접근 가능한 객체를 스캔
- 각각이 어떤 객체를 참조하고 있는지 탐색
3. Compact
: 객체들을 가까운 곳으로 모으고 Heap 메모리의 아래 쪽으로 보냄
- 바로 옆에 할당된 객체라고 해서 같은 시점에서 메모리에서 해제되지는 않음
-> 메모리가 GC를 거쳐서 파편화가 될 수 있음
- 파편화를 최소화하기 위해 JVM은 Major GC 때마다 Generation에 Compaction을 수행
cf) 파편화
: 메모리 내 비어있는 공간이 서로 떨어져 있어 크기가 큰 객체를 할당하기 어려워지는 것
GC가 중요한 이유
- Minor GC의 경우 보통 대략 0.5초에서 1초 이내에 끝나기 때문에 큰 문제가 되지 않음
- Full GC의 경우 시간이 많이 소요되며, 자바 애플리케이션이 멈춰버릴 수 있기 때문에 문제가 발생
- 멈추는 동안 사용자의 요청이 Queue에 들어있다가, 순간적으로 요청이 한꺼번에 들어오기 때문에 과부하에 의해 여러 장애를 만들 수 있음
- 원활한 서비스를 위해서 GC가 어떻게 일어나게 하느냐가 시스템의 안정성과 성능에 큰 변수로 작용
다양한 GC의 알고리즘
- JVM 버전 변화에 따라 여러가지 GC 방식이 추가되고 발전되어 옴
- 버전 별로 지원하는 GC는 차이가 존재하며, 서비스 상황에 따라 필요한 GC 방식을 JVM 옵션을 통해 설정으로 사용 가능
1. Serial GC
- 주로 32bit JVM에서 돌아가는 Single Thread 애플리케이션에서 사용
-> 별도로 지정하지 않을 경우 기본 GC
- Major GC, Minor GC 모두 Stop The World하는 Single Thread 방식
- Mark Sweep Compact 알고리즘
- Mark : Old 영역으로 이동된 객체들 중 살아있는 객체를 식별
- Sweep : Old 영역의 객체들을 훑는 작업을 수행하여 쓰레기 객체를 식별
- Compaction : 살아남은 객체들이 연속되게 쌓이도록 힙의 가장 앞 부분부터 채워서 객체가 존재하는 부분과 없는 부분으로 나눔
2. Parallel GC(Throughput GC)
- 기본적으로 Serial GC와 동작 과정은 동일, Minor GC에서 사용되는 Thread 수를 늘릴 수 있음
- 일반적으로 Minor GC만 멀티 스레딩, Major GC는 Single Thread으로 수행
- Multi Thread 사용
- GC의 오버헤드는 상당히 줄여주었지만, 애플리케이션이 멈추는 것은 피할 수 없음
- 메모리가 충분하고 코어의 개수가 많을 때 유리
- JAVA 8의 기본 GC
3. Parallel Old GC
- Parallel GC와 비교하여 Old 영역의 알고리즘만 다름
-> Major GC도 멀티 스레딩
- Mark Summary Compaction 단계를 거침 )
- Summary 단계는 앞서 GC를 수행한 영역에 대해 별도로 살아있는 객체를 식별한다는 점에서 Mark Sweep Compaction 단계와 다르며, 조금 더 복잡함
- Mark Sweep Compact : 단일 스레드가 Old 영역 검사
- Mark Summary Compact : 여러 스레드가 Old 영역 검사
- Mark : Old 영역을 Region 별로 나누고 Region 별로 살아있는 객체를 식별
- Summary : Region별 통계 정보로 살아있는 객체의 밀도가 높은 부분이 어디까지인지 Dense Prefix를 정함, 오랜 기간 참조된 객체는 앞으로 사용할 확률이 높다는 가정 하에 Dense Prefix를 기준으로Compact영역을 줄임
- Compact : Destination과 Source로 나누며 살아있는 객체는 Destination으로 이동시키고 참조되지 않는 객체는 제거
- 엄밀히 말하면 JAVA 8의 디폴트 버전은 Parallel Old GC
4. CMS Collector(Concurrent Mark-Sweep)
- Major GC에 소요되는 작업을 애플리케이션을 멈추고 진행하는 것이 아니라, 일부는 애플리케이션이 돌아가는 단계에서 수행하고 최소한의 작업만을 애플리케이션이 멈췄을 떄 수행하는 방식
- Initial Mark 단계 : 클래스 로더에서 가장 가까운 객체 중 살아있는 객체만 찾는 것으로 끝냄(멈추는 시간이 매우 짧음)
- Concurrent Mark 단계 : 참조 상태 객체를 확인, Stop The World 없이 다른 스레드가 실행 중인 상태에서 동시에 실행됨
- Remark 단계 : Cocurrent 단계에서 새로 추가되거나 참조가 끊긴 객체를 확인
- Concurrent Sweep 단계 : 참조되지 않은 객체를 정리(쓰레기 정리), 다른 스레드가 실행되고 있는 상태에서 진행됨
- Stop The World 시간이 매우 빠름
- 응답 속도가 매우 중요할 때 짧음
- Low Latency GC라고도 불림
- 단점)
- 메모리와 CPU 많이 사용
- 메모리 파편화가 단점
- Compaction 단계가 기본적으로 제공되지 않음
5. G1 Collector(Garbae First)
- 바둑판처럼 Heap 메모리 영역을 나누어 관리
- 기존 Young, Old 영역의 개념과 다른 Heap에 Region 개념을 도입
- Humonogous : Region 크기의 50%를 초과하는 큰 객체를 저장하기 위한 공간
- Available / Unused : 아직 사용되지 않은 Region
- 하나 이상의 Region에서 객체를 복사하여 다른 Region으로 이동시키는 방식
- CMS와 비슷한 방식으로 동작 시작
- Heap에 전역적으로 Marking
- 가장 많이 공간이 있는 곳부터 메모리 회수 진행 -> 이 때문에 Garbage First라는 이름이 붙음
- CMS Collector의 CPU 리소스 및 메모리 파편화의 단점 해결
- JAVA 9부터 기본 GC
G1 GC의 Cycle
- Young-Only Phase와 Space Reclamation Phase를 반복
- 사이클 중 모든 원 : Stop The World, 크기에 따라 소요시간이 달라짐
- 파란 원 : Minor GC
- 주황 원 : Major GC
- 빨간 원 : Mixed GC
- Space Reclamation Phase가 끝나면 다시 Young Only Phase로 돌아가서 Minor GC를 수행
1) Young Only Phase
- Minor GC만 수행하다가 Old Generation 비율에 지정된 값을 초과하는 순간 Major GC가 수행됨
- Major GC의 첫 단계는 Initial Mark이며, Minor GC와 동시에 수행되며 둘 다 STW를 수반하므로 다른 파란 원보다 큼
- 그 이후에 애플리케이션 스레드, Minor GC, Concurrent Mark가 동시에 수행되는데 Remark가 수행되는 순간 다른 작업은 멈추게 됨
-> Remark에 해당하는 주황색이 원이 큰 것을 알 수 있음
- 그 이후에 자잘하게 Minor GC가 수행되다가 Major GC의 Cleanup이 발생
2) Space Reclamation Phase
- Young Only Phase가 끝나고 시작됨
- Mixed GC : Mark 단계가 없어서 STW 빈도가 Young Only Phase에 비해 줄어듦
'JAVA' 카테고리의 다른 글
6. Xms & Xmx (0) | 2025.03.28 |
---|---|
5. JRE, JDK, JVM (0) | 2025.03.21 |
3. JVM 메모리 구조 (0) | 2025.03.14 |
2. 객체지향 (0) | 2025.03.14 |
1. Gradle (1) | 2025.03.14 |