반응형
Java Garbage Collection
1. 가비지 컬렉션 과정 - Generational Garbage Collection
1.1. 'stop-the-world'
- GC를 실행하기 위해 JV 이 어플리케이션 실행을 멈추는 것
- GC를 수행하는 쓰레드를 제외한 모든 쓰레드는 작업을 멈춤
- GC튜닝이란 stop-the-world 를 줄이는 것이다.
1.2. 명시적 해제
- 명시적으로 해제하려고 null을 할당하거나 System.gc()를 호출하는 경우도 있음 → System.gc()는 성능에 매우 큰 영향을 끼친다.(아마도 gc를 하면 stw 가 발생하여서 그런 것 같다.)
1.3. 두 가지 가설에 기반한 가비지 컬렉션
- 대부분의 객체는 금방 접근 불가능 상태가 된다.
- 왜??? 왜 이런 가설이?
- 오래된 객체에는 젊은 객체로의 참조는 아주 적게 존재한다.
1.4. Young 영역과 Old 영역
- 두가지 물리적 공간으로 나뉨
- Young / Old
- Young 영역
- 새롭게 생성한 객체의 대부분이 여기에 위치
- 대부분의 객체가 금방 접근 불가능 상태가 되기 때문에 매우 많은 객체가 Young 영역에 생성되었다가 사라짐
- 왜 금방 접근 불가능 상태?
- 객체가 사라질 때 Minor GC가 발생함
- Old 영역
- 접근 불가능 ㅅ아태가 되지 않아 Young 영역에서 사라 남은 객체가 복사됨
- Young 보다 크게 할당하며, 크기가 큰 만큼 Young 보다 GC는 적게 발생함
- 왜 크게 할당?
- Old 영역에서 GC가 발생하면 Major GC(Full GC)가 발생함
- Permanent Generation 영역
- Method Area라고도 한다.
- 이곳에서 GC가 발생하면 Major GC 가 발생
- JAVA8 이상에서는 사라짐
- Old 영역에 있는 객체가 Young 영역의 객체를 참조하는 경우
- Old 영역에는 512바이트짜리 카드 테이블이 존재
- Old 영역 객체가 Young 영역 객체를 참조할 때마다 정보를 표시
- Young 영역 GC 실행 시 Old 영역을 뒤지는 것이 아니라 카드 테이블을 참조하여 GC 대상 선별
1.5. Young 영역의 구성
- Eden 영역 / Survivor 영역 (2개) , 총 3개 영역으로 나뉨
- 새로 생성한 객체는 Eden 영역에 위치
- GC가 발생한 후 살아남은 객체는 Survivor 영역 중 하나로 이동된다.
- 하나의 Survivor 영역이 가득 차게 되면 그중에서 살아남은 객체를 다른 Survivor 영역으로 이동한다.
- 그리고 가득 찼던 Survivor 영역은 아무 데이터도 없는 상태로 된다.
- 실제로는 Survivor 포인터를 가지고 단순 변경이기 때문에 Minor GC 때에 리소스가 많이 들지 않는다.
- 이 과정을 반복하다 살아남은 객체를 Old 영역으로 이동시킨다.
- Survivor 영역 중 하나는 반드시 비어 있어야 한다.
1.6. Old 영역에 대한 GC
- Serial GC
- Parallel GC
- Parallel Old GC(Parallel Compacting GC)
- Concurrent Mark & Sweep GC(이하 CMS)
- G1(Garbage First) GC
2. GC의 종류
2.1. Serial GC
- JDK 5, 6에서 기본으로 사용되는 GC이다. 싱글 스레드로 Minor GC와 Major GC를 수행하기 때문에 Stop-The-World 시간이 다른 GC에 비해 상대적으로 긴 편이다.
- Serial GC를 서버 환경에서 사용하는 것은 바람직하지 않다. 하지만 빠른 응답속도가 필요하지 않거나 클라이언트 스타일의 장비에선 사용할 수도 있다.
2.2. Parallel GC
- Minor GC를 수행할 때 멀티 스레드를 사용하는 GC이다.
- 기본적으로 JVM이 구동되는 환경의 CPU 코어 개수에 맞춰서 N개의 GC가 수행된다
- Parallel GC를 활성화해도 장비의 CPU가 단일 코어면 Serial GC로 동작한다.
2.3. Parallel Old GC
- Parallel Old GC는 JDK 5 update 6부터 제공한 GC 방식
- Parallel GC와 비교하여 Old 영역의 GC 알고리즘만 다르다.
- Mark-Summary-Compaction 단계
- 마킹 → Summary → Compaction
- Mark-Sweep-Compaction 알고리즘의 Sweep 단계와 다르며, 약간 더 복잡한 단계를 거친다.
2.4. Concurrent Mark Sweep(CMS) GC
- CMS GC는 Old 영역을 대상으로 이루어진다
- Initial Mark 단계 → Concurrent Mark 단계 → Remark 단계→ Concurrent Sweep 단계
- Initial Mark 단계 : GC Root에서 참조 Tree에서 제일 상단에 있는 객체만 선별하는 과정. 탐색 깊이가 얕아서 STW 시간이 매우 짧다
- Concurrent Mark 단계 : Initial Mark 단계에서 살아남은 객체의 참조를 따라가며 살아있는 객체를 찾는다, 이 단계의 특징은 다른 스레드가 실행 중인 상태에서 동시에 진행된다는 것
- Remark 단계 : Remark에서는 Concurrent Mark를 수행하는 동안 객체의 참조가 끊기거나, 새롭게 생긴 객체가 없는지 다시 한번 확인한다.
- Concurrent Sweep 단계 : 쓰레기를 정리하는 작업을 실행한다. 이 작업도 다른 스레드가 실행되고 있는 상황에서 진행한다.
- 멀티 쓰레드와 작업 단위 특징상 STW 가 아주 짧다.
- 단점으로는
- 다른 GC 방식보다 메모리와 CPU를 더 많이 사용한다.
- Compaction 단계(조각 모음이라고 보면 됨)가 기본적으로 제공되지 않는다.
2.5. G1 GC
- CMS GC와 다른 점은 G1 GC는 메모리를 페이징 하듯이 논리적인 단위(Region)로 나눠서 관리한다
- Region이라는 논리적인 단위로 메모리를 관리하여 CMS와 달리 Compaction 단계를 진행하고 메모리 단편화 문제 제거
- STW의 시간을 예측할 수 있다는 것이 G1 GC의 큰 장점
- G1 GC는 Initial Mark 단계를 멀티 스레드로 수행한다.
- 이 단계 후에 G1 GC는 Region 중 어떤 Region이 GC후에 메모리를 많이 반환할지 알게 된다.
- G1 GC는 비 참조 객체를 수집하며 살아있는 객체는 하나의 Region에 이동하는 작업을 진행하는데, 이 과정에서 Compaction이 이루어진다.
2.5.1. G1의 Young GC
- Young GC가 발생하면 Young GC에서 살아남은 객체들이 한 개 이상의 Survivor Region에 차곡차곡 이동(이 과정에서 STW가 발생)
- Young GC가 끝난 후에는 살아남은 객체가 Survivor 혹은 Old Region으로 이동되어 Compaction 된 상태가 된다.
2.5.2 G1의 Old GC
- 앞에서 소개한 다른 GC와 다르게 G1 GC는 살아있는 객체(live object)를 찾아서 비어있는 Region에 이동 후 남은 객체들을 삭제하는 방식을 사용
- Initial Mark : STW가 발생한다. 보통 Young GC의 Mark 단계에서 같이 이루어지는데, Survivor Region 중 Old 영역의 객체를 참조하고 있는 객체가 존재하는 Region을 식별한다.
- Root Region Scanning : Initial Mark 단계에서 식별한 Region에서 Old 영역의 객체를 참조하고 있는 Young 영역의 객체를 식별한다.
- Concurrent Marking : Heap 전체에 걸쳐 참조되고 있는 객체(live object)를 찾는다.
- Remark : STW가 발생한다. Concurrent Mark 단계의 결과를 검증한다.
- Cleanup & Copying : 참조되고 있는 객체(live object)를 비어있는 영역으로 옮기고, 참조되지 않는 객체들을 삭제 후에 비게 된 영역을 Unused 상태로 만든다, 이때, 참조되고 있는 객체들을 비어있는 영역으로 옮기는 과정에서 STW가 발생한다.
반응형
'IT > JAVA' 카테고리의 다른 글
스프링 빈 생명주기 메서드와 실행 순서 (0) | 2021.04.12 |
---|---|
ThreadLocal에 대해서 알아보자 - 1 (0) | 2021.03.12 |
Java HashMap은 어떻게 동작하는가? (0) | 2020.11.05 |
Filter 를 활용한 ACL 만들기 (feat. Spring) (0) | 2019.11.15 |
Spring Boot Resource 사용시 접두사(classpath, file 등)를 사용해야 하는 이유 (2) | 2019.10.25 |