반응형
Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
Tags
- Spring
- Kafka
- Scaffold
- 부하 테스트
- 운영체제
- 디프만
- flutter
- c
- exception
- pub.dev
- 코딩 테스트
- Redis
- 코딩
- 코드 트리
- depromeet
- 자료구조
- OAuth
- 코딩테스트
- C언어
- AOP
- 연습문제
- Kotlin
- nGrinder
- Sharding
- 코드트리
- Oidc
- java
- dip
- 디프만16기
- kakao
Archives
- Today
- Total
Nick Dev
[JAVA] Heap 구조, GC 동작 방식 그리고 GC 알고리즘 5가지 본문
반응형
Garbage Collector 역할
- 메모리 할당
- 사용 중인 메모리 인식
- 사용하지 않는 메모리 인식
GC 종류 2가지
Minor GC
- Eden 영역이 꽉 차게 되면 시작되는 GC
- Young generation에서 발생하는 GC
- 과정
- Eden이 처음 꽉 차게 되면 Minor GC 이후 살아남은 객체들 S0로 이동(S0로 가정)
- S1은 empty
- S0에 들어가기에 너무 큰 객체면 바로 Old영역으로 promotion
- 또 Eden이 꽉 차게 되면 Minor GC 발생
- 이때, Eden + S0 에서 살아남은 객체들이 S1으로 이동
- S0가 empty
- 1, 2의 과정 계속 반복
- 이렇게 Survivor 영역 사이 이동할 때마다 age 증가
- 객체가 특정 age 넘으면 Old generation으로 promotion
- HotSpot JVM의 경우 default = 15
-XX:MaxTenuringThreshold
옵션으로 이 연령 한계를 설정 가능
- Eden이 처음 꽉 차게 되면 Minor GC 이후 살아남은 객체들 S0로 이동(S0로 가정)
🌟 Survivor 영역이 2개로 나눠져서 한 쪽은 항상 비어있는 이유 🌟
- 디스크 조각 모음 기능과 유사하다
- 한 공간을 몰아서 정리하지 않으면 빈 공간이 많이 생겨, 전체 메모리 공간은 많아도 파편화 되서 실질적으로 메모리를 할당하지 못할 수 있다.
- GC로 메모리 청소하는김에 한 공간으로 몰아서 순차적으로 정리해놓으면 나중에 또 접근할 때 성능이 좋다.
- 그래서 이렇게 순차적으로 정리를 하기 위해서 하나의 Survivor 공간을 비워두고 거기에 정리해서 넣는 것
Old generation으로 promotion하는 경우 2가지
- S0 ↔ S1 이동할 때마다 증가하는 age가 임계값을 넘을 때
- 객체의 크기가 Survivor 영역보다 클 때
Major GC
- Full GC라고도 함
- Old generation이 꽉 차면 발생
- unreachable한 객체들 정리
Heap 영역
- 가비지 컬렉터가 인식하고 할당하는 영역
구조
- Young, Old, Perm 영역
- Perm 영역은 JDK 8부터 사라짐
Young 영역
- Eden 영역, S0, S1 영역으로 구성
- S는 Survivor의 약자
- S0, S1 사이에 우선순위 없음
Eden 영역
- 객체가 생성될 때, 가장 왼쪽에 있는 Eden 영역에 객체가 지정
- Eden 영역에 데이터(생성된 객체들)가 꽉 차면 → minor gc 발생
- 이때, 살아남은 객체들은 S0 또는 S1 영역으로 옮겨가야된다
- S0로 이동했다고 가정
Survivor0 or 1 영역
- Eden 영역이 꽉 차게 되면 객체들이 저장되는 공간
- S0와 S1 둘 중 한 공간은 항상 비어 있다
Old 영역
- Young generation에서 promotion되어 올라온 객체들
- Old가 꽉 차면 Major GC 발생
- 더 이상 참조되지 않는 객체들을 정리
5가지 GC 알고리즘
1. Serial Collector
- Minor GC 수행하고 Major GC 수행
- 이렇게 serial하게 처리
- 1개 CPU 사용
- 이렇게 GC가 수행될 때, 애플리케이션 수행이 중지 = stop the world
- Major GC는 Mark-Sweep-Compact 알고리즘으로 동작
- reachable한 객체 mark → mark 안된 것들 sweep → 살아남은 것들 Old의 맨 왼쪽에 compact
- 한쪽으로 압축하는 과정을 통해 memory fragmentation을 줄일 수 있다 (카페 예시)
정리
- 싱글 코어 or 싱글 thread일 때 사용하지만, 요즘 기기에는 이런 경우 없음 → 잘 안쓴다
2. Parallel Collector
Minor GC
- Parallel하게 처리
- Minor GC 수행할 때, 여러 thread가 병렬로 수행
- 이유 : Eden이 자주 꽉차서 Young 영역 GC가 빈번하게 발생 → 여러 thread가 처리하면 GC 작업 시간 감소 = STW 시간 감소 → 애플리케이션 처리량 증가
Major GC
- Mark-Sweep-Compact 알고리즘 사용(아직 Old 영역은 하나의 thread로 처리)
정리
- 멀티 코어, 프로세서 환경에서 사용
- Minor GC는 Parallel하게 처리하지만 Major GC는 single Thread로 처리
3. Parallel Compacting Collector
Minor GC
- Parallel Collector와 동일한 알고리즘으로 동작
- 즉, Paralle하게 처리
Major GC
- 새로운 Mark-Summary-Compact 알고리즘 사용
- Summary 부분이 기존 Sweep 부분과 다른 점 2가지
- 여러 thread가 Old 영역을 분리해서 훑는다
- 이전에 Major GC에 의해 Compacting된 영역을 별도로 훑는다
- 또 훑는 이유는 이 영역에 대해 추가적으로 압축이 필요한지 체크해 메모리 단편화를 감소시키기 위해서
정리
- 이젠 Minor, Major GC 모두가 멀티 쓰레드 방식으로 GC 수행
4. Concurrent Mark-Sweep Collector(CMS)
Minor GC
- Parallel Collector와 동일한 알고리즘으로 동작
- 즉, Paralle하게 처리
Major GC
- CMS GC 알고리즘 사용 (4단계)
- Initial Mark(STW)
- GC Roots로부터 직접 접근 가능한 객체들을 마킹
- stack 내의 local 변수들의 객체, 정적 변수들
- 얘네들만 marking하고 넘어감
- GC Roots로부터 직접 접근 가능한 객체들을 마킹
- Concurrent Mark
- Initial Mark에서 마킹한 애들 따라가면서 참조하고 있는 객체들 따라가면서 확인
- 다른 thread와 동시에 진행
- Remark(STW)
- Concurrent Mark가 다른 thread와 동시에 진행하기에 수행하는 동안 객체 참조가 끊길 수 있기에 다시 확인하는 단계
- 결국 reachable한 객체들만 marking
- Concurrent Sweep
- marking되지 않은 객체들 모두 정리
- 다른 thread들과 동시에 수행
정리
- Minor, Major GC 둘다 멀티 thread 지원
- Minor GC는 Parallel Collector와 동일한 알고리즘
- Major GC는 CMS 알고리즘 사용 → STW 매우 짧지만, Compaction이 없어서 메모리 단편화 발생
5. Garbage First Collector(G1)
- 앞선 GC들과 아예 다름
- Young, Old 주소가 물리적으로 Linear하게 있지 않고 바둑판 형식으로 구성
5번 GC
1~4 GC들
- JDK 9 이후 디폴트 GC로 권장
Young영역 GC (Minor GC)
- 특정 region들을 Young 영역으로 선정
- Eden region 꽉차면 GC 발생(STW)
- 병렬로 진행
- Young 영역의 객체들 스캔해서 reachable한 객체 식별 후 Survivor 영역으로 옮김
- 이때 age가 꽉찬 객체는 Old 영역으로
- 옮겨지고 난 영역은 빈 영역은 Eden, Survivor 영역으로 재활용
Old영역 GC (CMS의 GC와 유사)
- 특정 점유율에 도달하면 CMS 시작
- Initial Mark (STW)
- Old영역에 있는 객체를 참조하고 있는 Survivor 영역에 대해 Mark
- Root region scanning
- Survivor 영역을 스캔해 Old 영역에 대한 참조를 찾는다
- Young GC 발생 전에 이 단계 완료되어야 함
- 애플리케이션 수행과 동시에 진행
- Concurrent Mark
- 전체 Heap 영역에 살아있는(reachable한) 객체 찾기
- 애플리케이션 수행과 동시에 진행
- Young GC 발생하면 잠깐 멈춘다
- Remark (STW)
- STW 한 후, Heap에서 살아있는 객체들 표시
- SATB(snapshot-at-the-beginning) 알고리즘 사용
- CMS GC에서 사용하는 방식보다 빠름
- Cleaning (STW)
- 살아있는 객체와 빈 구역 식별 → 필요없는 객체(unreachable)들 지운다
- 빈 구역들 초기화 ( → 재사용 )
- Copy (STW)
- Compaction 진행 → 이거 때문에 CMS의 개선안이라고 할 수 있는 것 같음
- 살아 있는 객체들 빈 구역으로 모은다
- 메모리 단편화 줄일 수 있다
- Compaction 진행 → 이거 때문에 CMS의 개선안이라고 할 수 있는 것 같음
Snapshot-At-The-Beginning
- 마킹 시작 시점에 스냅샷 캡처
- 가비지 컬렉션의 초기 표시 단계에서, 모든 살아 있는 객체들을 "표시"
- 이 시점에서의 힙 상태가 스냅샷으로 간주
- 애플리케이션의 변경사항 로깅
- 객체를 참조하는 것에서 참조하지 않는 것으로 변경)할 때, 이러한 변경 사항을 로그에 기록
- 동시 마킹 단계
- 스냅샷 시점에서 살아 있었던 객체와 그 이후 로그에 기록된 참조 변경사항을 고려하여 살아 있는 객체를 식별
언제 사용?
- 대용량 Heap일 때
- G1은 Heap을 여러 region으로 나눠 관리하기에 Heap이 매우 크면 효율적으로 관리할 수 있다
- 멀티 프로세서, 코어 환경
- 메모리 단편화 줄이고 싶을 때
정리
- Heap 메모리가 바둑판처럼 되어 있어 Eden, Survivor, Old가 물리적으로 Linear하게 있지 않다
- Old영역 GC는 CMS Collector의 Old 영역 GC와 유사하지만
- Copy단계에서 Compaction을 통해 메모리 단편화를 줄여준다
- Remark 단계에서 SATB 알고리즘을 써서 더 빨리 reachable 객체들 표시한다
간단하게 정리
Young | GC 알고리즘 | Old (mark-sweep-compact) |
---|---|---|
single thread | Serial | single thread |
multi thread | Parallel | single thread |
multi thread | Parallel Compacting | multi thread |
multi thread | Concurrent Mark-Sweep | CMS(multi thread) |
아마 multi thread..? | G1 | CMS와 유사(SATB, Copy 단계) |
반응형
'Java' 카테고리의 다른 글
[JAVA] try-with-resources 써야 돼? (0) | 2024.12.11 |
---|---|
[JAVA] Exception과 Error, Checked와 Unchecked (0) | 2024.12.11 |
[JAVA] JVM의 Runtime Data Area (0) | 2024.12.11 |
[JAVA] Thread 생성과 핵심 메서드 (0) | 2024.12.11 |
[JAVA] Map, HashMap, TreeMap, Properties (0) | 2024.12.11 |