또한 가비지 컬렉션이 일어날 때는 GC 관련 스레드를 제외한 모든 스레드가 멈추게 된다.(Stop-The-World)
💡
약한 세대별 가설(Weak Generational Hypothesis)
대부분의 객체는 생성된 지 얼마 안 되어 바로 쓰레기가 된다! 그리고 일단 오래 살아남은 객체는 앞으로도 계속 오래 살아남을 가능성이 높다!
이 가설은 다음 표에서 나타나듯 많은 프로그램에서의 수명을 관찰하다가 나온 결론입니다.
가로축(x축): 객체의 수명 (얼마나 많은 바이트가 할당되는 동안 살아있었는지)
세로축(y축): 해당 수명을 가진 객체들의 총 바이트 크기
이런 특징을 바탕으로 힙 영역(메모리 공간)을 젊은 세대(Young Generation)와 오래된 세대(Old Generation)로 나누고, 각 세대에 맞는 효율적인 방식으로 메모리를 청소합니다. 이렇게 하면 매번 모든 객체를 검사할 필요 없이, Young 영역을 집중적으로 청소하여 훨씬 빠르게 쓰레기를 회수할 수 있게 됩니다.
Young 영역
Young 영역에는 Eden 영역과 두개의 Survivor 영역으로 나뉩니다. 이 세 공간이 어떻게 협력해서 객체를 처리하는 지 알아봅시다.
객체 생성: 대부분의 새로운 객체는 에덴 영역에 처음 만들어집니다.
첫 번째 가비지 컬렉션 (Minor GC)
에덴 영역이 꽉 차면, Minor GC라는 가비지 컬렉션이 시작됩니다.
이때 에덴 영역과 현재 사용 중인 서바이버 영역(S0)에 있는 객체들을 검사합니다.
쓰레기 객체: 더 이상 참조되지 않는 쓰레기 객체들은 그냥 사라집니다. (메모리가 회수되죠.)
살아있는 객체: 아직 사용 중인 객체들은 비어있는 다른 서바이버 영역(S1)으로 복사됩니다.
이 과정이 끝나면, 에덴 영역과 원래 사용 중이던 서바이버 영역(S0)은 깨끗하게 비워집니다.
다음 가비지 컬렉션
이제 에덴 영역이 다시 채워지고, 다음 마이너 GC가 발생합니다.
이번에는 두 서바이버 영역의 역할이 바뀝니다. 이전 GC에서 채워졌던 서바이버 영역(S1)이 "원본"이 되고, 나머지 비어있던 서바이버 영역(S0)이 "목적지"가 됩니다.
에덴 영역과 이전 원본 서바이버 영역(S1)의 살아있는 객체들은 다시 비어있는 서바이버 영역(S0)으로 복사됩니다.
이 과정을 통해 객체들은 서바이버 영역 사이를 왔다 갔다(복사) 하면서 "나이(age)"를 먹습니다. 즉, GC 사이클을 몇 번이나 견뎠는지 카운트됩니다.
객체의 노화 (Aging) 및 승진 (Promotion)
이렇게 서바이버 영역 사이를 여러 번 왔다 갔다 하면서 살아남은 객체들, 즉 일정 횟수 이상 GC를 버텨낸 객체들은 "오래 살아남을 가능성이 높은 객체"로 간주됩니다.
이런 객체들은 결국 올드 세대로 이동(승진)하게 됩니다.
또는, 객체 하나의 크기가 너무 커서 서바이버 영역에 들어갈 공간이 부족한 경우에도 바로 올드 세대로 이동할 수 있습니다.
💡
Eden → (Survivor1 ↔ Survivor2) → Old
Old 영역
오랫동안 살아남은 객체들이 모이는 곳입니다. 영 세대보다 훨씬 크게 설정되며, 가비지 컬렉션도 영 세대만큼 자주 일어나지 않습니다. 올드 세대에서 발생하는 GC는 Major GC 또는 Full GC라고 부르는데, 이는 힙 전체를 검사하기 때문에 마이너 GC보다 훨씬 많은 시간과 자원을 소모할 수 있습니다.
대부분의 GC 알고리즘(가비지 컬렉터 종류)은 위 개념을 따르지만 각 GC 알고리즘은 이 기본 구조를 어떻게 구현하고 어떤 전략으로 메모리를 관리하느냐에 따라 힙의 내부 구조나 동작 방식에 큰 차이를 보입니다.
최근 자바버전의 공식문서를 살펴보면 G1 가비지 컬렉터를 사용하는 걸 알 수 있습니다.
Garbage-First Garbage Collector 자바 애플리케이션의 메모리를 관리하는 특별한 "쓰레기 청소부"입니다. 다른 청소부들보다 좀 더 똑똑하고, 특히 큰 메모리(힙)를 다룰 때 빛을 발합니다.
![image.png]
Eden (빨간색): 대부분의 새로운 객체는 여기에 처음 만들어집니다.
Survivor (빨간색, "S" 표시): 에덴에서 살아남은 객체들이 잠시 머무는 곳입니다.
Old (옅은 파란색): 젊은 세대에서 오랫동안 살아남아 '승진'한 객체들이 모이는 곳
H (옅은 파란색, "H" 표시): 특별히 매우 큰 객체
기존에는 메모리를 Young / Old 만 나누었지만 이 알고리즘에서는 각 메모리를 작은 구역으로 쪼개고 각 구역들은 필요에 따라 Eden, Survivor, Old 가 될 수 있습니다.
이런 메모리 구조덕분에 큰 객체가 들어와도 흩어져서 할당할 수 있고 이 블록은 그때그때 필요한 역할을 맡을 수 있어서, 메모리 할당과 청소에 훨씬 더 유연하고 효율적입니다.
→ 기존 GC처럼 힙 전체를 한 번에 스캔하고 처리하는 대신, G1은 이 개별 영역 단위로 GC 작업을 수행
→ Stop-The-World 시간 감소
동작과정
G1 GC는 메모리를 청소하는 데 크게 두 가지 단계를 번갈아 가며 수행합니다. 마치 '평상시 쓰레기통 비우는 단계'와 '대청소 단계'로 나누는 것과 비슷합니다.
1. Young-Only : 평상시 쓰레기통 비우기
시작: 이 단계는 몇 번의 Normal young collection 으로 시작합니다.
Normal young collection : 새로 만들어진 객체들이 있는 Young 영역을 주로 청소하고, 여기서 오래 살아남은 객체들은 Old 영역에 쌓아둡니다.
위에서 살펴본 Minor GC 과정이네요.
Concurrent Start : 대청소 준비
Old 영역이 특정 비율(시작 힙 점유율 임계값, Initiating Heap Occupancy threshold) 이상으로 채워지면, G1은 이제 '대청소'를 준비해야겠다고 생각합니다.
이때 G1은 Normal young collection 대신 Concurrent Start young collection을 실행합니다.
Concurrent Start young collection : 이 컬렉션은 Young 영역 청소와 더불어, Old 영역에 있는 객체들 중에서 살아있는 객체가 무엇인지를 찾아내는 작업(마킹 프로세스)을 애플리케이션이 돌아가는 동안(동시적으로) 시작합니다.
마킹 작업이 완전히 끝나기 전에도 일반 영 컬렉션은 계속 발생할 수 있습니다.
특이 케이스: 만약 이 '동시 시작' 단계에서 "어? Old 영역에 별로 쓰레기가 없는데?"라고 G1이 판단하면, 마킹 작업을 중단하고 다시 영-온리 단계로 돌아가 평상시 청소를 계속합니다. (이러면 아래의 Remark/Cleanup은 발생하지 않습니다.)
마킹 마무리 (Remark & Cleanup):
동시 마킹 작업이 거의 끝나면, 두 번의 짧은 Stop-The-World 가 발생합니다.
Remark: 마킹 작업을 최종적으로 마무리하고, 사용되지 않는 클래스나 참조들을 정리하며, 완전히 비워진 구역들을 회수합니다. 이 단계에서 G1은 나중에 동시적으로 회수할 메모리 공간에 대한 정보도 계산합니다.
Cleanup: 이 단계에서 대청소 단계(Space-Reclamation phase)로 들어갈지 최종적으로 결정합니다. 만약 대청소가 필요하다고 판단되면, G1은 마지막으로 Young 영역을 청소하며 Young-Only 단계를 마무리합니다.
2. 공간-회수 단계 (Space-Reclamation phase): 본격적인 대청소
시작: 이 단계에서는 Mixed collection 이 여러 번 발생합니다.
Mixed collection : 이름 그대로, Young 영역을 청소하는 것뿐만 아니라, Old 영역에서 쓰레기가 많은 곳들을 선택해서 함께 청소합니다.
필요한 부분만 효율적으로 청소하여 애플리케이션의 중단 시간을 최소화
이 과정에서 G1은 가장 효율적으로 메모리를 회수할 수 있는 Old 영역을 우선적으로 복사해서 이동시킵니다.
💡
이 과정의 핵심은 Young 영역에서 살아남은 객체들을 Old 영역으로 복사(Evacuation)
하는것은 세대이동에 의의가 있고 Old 영역에서의 복사는 메모리 압축에 의의가 있습니다.
앞에서 말했듯이 메모리 구조를 작은구역을 쪼개었기 때문의 기존에 비해서 메모리 단편화 문제에 자유로워 진 것은 사실이나, 아예 없는 것은 아니라고 합니다. 만약에 Old 영역을 압축하지 않고 있다가 메모리 단편화 현상이 발생한다면 그 때는 Full GC 가 실행되는데 이는 기존 GC와 마찬가지로 힙 전체를 압축하는 매우 긴 STW 를 유발합니다.