GC(Garbage Collection)란?
JVM에서 메모리를 관리해 주기 때문에 더 이상 사용하지 않는 객체를 청소해 주는 작업이다.
Java의 Heap 메모리 구조
Java의 Heap 메모리는 객체가 동적으로 생성되고 관리되는 메모리 영역이다. 이 메모리는 크게 Young 영역과 Old 영역으로 나뉜다. Young 영역은 다시 Eden 영역과 Survivor 영역으로 세분화된다.
1. Young 영역
Young 영역은 상대적으로 짧은 생애를 가진 객체들이 위치하는 영역이다. 이 영역은 두 부분으로 나뉜다.
- Eden 영역: 새로운 객체가 생성되는 영역이다. 모든 객체는 처음에 Eden 영역에 생성된다.
- Survivor 영역: Eden 영역에서 살아남은 객체들이 이동하는 영역이다. Survivor 영역은 두 개로 나뉘며, 하나는 현재 사용 중이고 다른 하나는 비어 있다.
GC가 발생하면 Eden 영역에서 살아남은 객체들은 Survivor 영역으로 이동한다. Survivor 영역에서 계속 살아남은 객체들은 Survivor 영역의 사용량이 증가하고 빈 Survivor 영역이 채워지는 방식으로 반복적으로 이동된다.
2. Old 영역
Old 영역은 오래 살아남은 객체들이 위치하는 영역이다. Young 영역에서 여러 번 GC를 거치면서도 여전히 살아남은 객체들은 Old 영역으로 이동한다. 객체가 Old 영역으로 이동하는 이유는 다음과 같다.
- Young 영역의 Eden 영역과 Survivor 영역의 크기보다 객체가 클 경우.
- GC를 반복하면서 계속 살아남은 객체들이 누적되기 때문.
3. Full GC
Old 영역의 메모리가 꽉 차면, Full GC가 발생한다. Full GC는 Heap 메모리 전체를 대상으로 하는 GC로, Young 영역과 Old 영역 모두를 검사하여 불필요한 객체를 제거한다. 이 과정으로 Old 영역의 메모리 사용량을 줄이고, 메모리 누수를 방지할 수 있다.
4. Metaspace
예전에 Permanent 영역이 있었으나, Java 8부터는 Metaspace로 변경되었다. Metaspace는 클래스 메타데이터와 관련된 정보를 저장하는 영역으로, Heap 영역과는 별개로 관리된다.
이러한 메모리 구조와 GC 방식은 Java 애플리케이션의 성능에 큰 영향을 미친다. Heap 메모리의 적절한 관리와 GC 전략을 통해 애플리케이션의 메모리 사용을 최적화하고 성능을 개선할 수 있다.
대부분은 Eden영역에서 죽는다.
GC모니터링 방법
jstat
https://docs.oracle.com/en/java/javase/20/docs/specs/man/jstat.html 콘솔에서 간단히 JVM Heap 메모리 현황 모니터링 가능하다.
Garvage Collector의 종류
Serial Collector
- GC를 단일 스레드로 실행
- 스레드 간 GC 오버헤드가 발생하지 않음
- 단일 프로세서 장비 적합
- 옵션:-XX:+UseSerialGC
Parallel Collector
- GC를 여러 스레드로 실행
- 옵션
- -XX: +UseParallelGC
- 이 옵션은 Compaction 작업을 기본적으로 수행함
- Compaction을 끄기 위해서는 -XX:-UseParallelOldGC 옵션을 추가하 면 됨
참고) Compaction 작업 →메모리가 조각나 있는 것들을 한 곳으로 모으는 작업 시간과 CPU를 많이 사용함
G1 GC
- G1 GC란?
Shenandoah GC - CPU 많이 씀
- Red Hat에서 개발
- OpenJDK 12에 추가
- low-pause GC
- Shenandoah adds concurrent compaction.
- 옵션: -XX:+UseShenandoahGC
ZGC
- 확장 가능한 빠른 가비지 컬렉터
- 적용 가능한 힙 크기 : 8MB ~ 16TB
- 최대 대기 시간은 밀리초 단위를 목표로 함
- 옵션 :-XX:+UseZGC
- https: /openjdk.org/projects/zgc/
GC는 하나만 지정할 수 있다.
JDK 17 버전 기준으로는 CPU가 1개라면 Serial GC가 실행되고, CPU가 2개 이상이라면 G1 GC가 실행된다.
(사실 JDK 9 버전 이후로 동일하다.)
그렇다면 해당 GC들은 어떻게 동작하는지 알아보자.
FAQ
- GC 옵션을 모든 서비스(JVM)에 대해서 다 동일하게 설정했는데 그래도 되나요?
- 옆에 있는 동료와 내 인생이 동일한가? = 서비스마다 다르다.
- 메모리 크기를 어떻게 잡는 것이 좋을까요?
- 메모리 크기가 크면? = GC 할 때 체크하는 시간이 많이 든다
- 메모리 크기가 너무 작으면? = 실제 복잡한 작업(메모리가 많이 소요되는)이 있으면 죽어버린다 outofmemory. 2~4기가
- Od 영역 메모리 사용량이 늘어나기만 해요. 괜찮은가요?
- Old 영역의 메모리 사용량은 현 상황만 보면 안 됨!
- Full GC
- FullGC 이후로 0d 영역이 계속 부족해져요. 어떻게 해야 하나요?
- Heap dump로 확인
- Concurrent Mark and Sweep GC라는 것이 있던데요.
- Java 9 이상에서는 G1 GC를 사용하는 것을 권장함
- CC 시간 때문에 고려해야 할 것?
- Timeout
- CPU
G1GC
기존의 GC가 Pause Time(Stop the world)을 극단적으로 가져가지 않기 위해 구현되었다면, G1GC는 FullGC를 최대한 피하기 위해 Pause Time을 짧게 가져가기 때문에 준 실시간 성능을 구현하였다.
- 높은 처리량과 낮은 Stop-The-World(STW) 지향
- CMS의 개선안으로 계획됨
- 쓰레기 비중이 높은 heap region을 집중적으로 수집한다.
- Java9부터 디폴트로 설정되어 있다.
- XX:+UseG1 GC옵션을 사용하여 수동으로 활성화할 수 있다.
새롭게 정의된 Humongous, Available/Unused 영역이 존재한다.
- Humongous : Region 크기의 50%를 초과하는 큰 객체를 저장하기 위한 공간. 이 Region에서는 GC가 최적으로 동작하지 않는다.
- Available/Unused : 아직 사용되지 않은 Region을 의미한다.
G1GC에서 Full GC가 수행될 때 흐름
G1GC에서 Full GC가 수행될 때 Initial Mark ->Root Region Scan -> Concurrent Mark -> Remark -> Cleanup -> Copy 단계를 거치게 된다. 추가적으로, G1GC는 일시 정지 시간을 줄이기 위해 각 스레드가 자신만의 region을 잡고 작업하는 방식으로 병렬 GC를 수행한다.
- Initial Mark: Old Region에 존재하는 객체들이 참조하는 Survivor Region을 찾는다. 이 과정에서는 STW 현상이 발생하게 된다.
- Root Region Scan: Initial Mark에서 찾은 Survivor Region에 대한 GC 대상 객체 스캔 작업을 진행한다.
- Concurrent Mark 전체 힙의 Region에 대해 스캔 작업을 진행하며, GC 대상 객체가 발견되지 않은 Region 은 이후 단계를 처리하는데 제외되도록 한다.
- Remark 애플리케이션을 멈추고(STW) 최종적으로 GC 대상에서 제외될 객체(살아남을 객체)를 식별해 낸다.
- Cleanup 애플리케이션을 멈추고(STW) 살아있는 객체가 가장 적은 Region에 대한 미사용 객체 제거를 수행한다. 이후 STW를 끝내고, 앞선 GC 과정에서 완전히 비워진 Region을 Freelist에 추가하여 재사용될 수 있게 한다.
- Copy GC 대상 Region이었지만 Cleanup 과정에서 완전히 비워지지 않은 Region의 살아남은 객체들을 새로운(Available/Unused) Region에 복사하여 Compaction 작업을 수행한다.
'JAVA' 카테고리의 다른 글
캡슐화에 대한 정리 with 세션 (0) | 2024.09.26 |
---|---|
JVM 동작 방식 (0) | 2024.09.20 |
UncheckedException과 CheckedException (1) | 2024.09.13 |
ArrayList는 어떻게 크기가 조절될까? (0) | 2024.09.11 |
Java final과 불변성 (0) | 2024.09.08 |
가비지 컬렉션이란? (1) | 2024.09.06 |
제네릭이란? (2) | 2024.09.04 |
인터페이스와 추상 클래스 (3) | 2024.09.03 |