CS/OOP

GC(garbage collection) 알아보기

쪼멘탈 2022. 10. 11. 14:29
반응형

 

GC 알아보기

 

GC(garbage collection)이란?

Garbage Collection은 보통 메모리의 압박이 있을 때 수행하게 된다. 어떤 이유에서든지 메모리가 필요하면 수행된다는 의미다. GC는 새로운 Object의 할당을 위해 한정된 Heap 공간을 재활용하려는 목적으로 수행된다. Heap영역을 재활용하기 위해 Root Set에서 참조되지 않는 Object를 없애 가용한 공간을 만드는 작업이라고 할 수 있다. 재활용을 위해 수행된 메모리의 해지는 할당한 그 자리에서 이루어지기 때문에 Garbage가 빠져나간 자리는 듬성듬성 비어있다. 이 경우 메모리의 개별 Free Space의 크기보다 큰 Object에게 공간을 할당할 경우 재활용의 의미가 사라진다. 

 

GC 작동원리

  1. 객체를 생성할 때마다 스택에 변수 이름을 저장하고 관련된 힙에 객체를 저장한다.
  2. 위의 그림에서 객체가 Heap에 저장된 부분에서 Reachable And Live를 녹색, Unreachable을 빨간색으로 표시했다.
  3. Heap에서 녹색으로 표시된 개체는 힙에 있는 다른 개체에 대한 강력한 참조를 갖고 있으며 빨간색으로 표시된 개체는 GC의 대상이 된다.

GC 메커니즘

  • Object는 생성된 후 금방 Garbage가 된다. (high infantmortality)
  • Older Object가 Younger Object를 참조할 일은 드물다

GC 대상 및 범위

GC의 대상은 Young Generation, Old Generation과  Permanent Area로 나누어져 있다. 여기서 Young Generation에서 발생하는 GC를 Minor GC, Old Generation과  Permanent Area에서 발생하는 GC를 Full GC(Major GC)라고 한다. Young Generation은 빈번하게 GC가 발생하는 영역이고 Young Generation안에 있는 Object가 특정 회수 이상 참조되어 기준 Age를 넘는다면 Old Generation으로 이동한다. 만약 이동할 때 Old Generation의 메모리가 충분하지 않다면 Old Generation에도 GC가 발생한다. Permanent Area에 메모리가 부족하면 Heap(Young + Old)에 Free Space가 많더라도 Full GC가 발생한다. 

 

Heap 메모리 구조

  • Young Generation : 신규 생성 객체가 존재하며, 다시 3개의 영역(Eden, Survivor1, Survivor2)으로 나뉘어 있다. 빨리 객체가 생겼다가 사라지는 영역이다. 더 이상 참조되지 않는 객체는 Young GC(Minor GC)를 통해 제거된다.
  • Old Generation : Young Generation에서 GC 되지 않고 살아남은 객체가 이동하는 영역으로 오랫동안 유지되는 객체가 존재한다. 더 이상 참조되지 않는 객체는 Full GC(Major GC)를 통해 제거된다.
  • permanent Generation, Metaspace : Heap에 있는 객체에 대한 메타데이터 정보가 저장된다. 클래스 자체에 대한 정보가 저장된다. 기본적으로 JVM마다 64MB의 공간을 할당한다. PermGen 영역은 Heap 영역은 아니며, Non-Heap 영역이지만 PermGen 영역도 Full GC 발생 시 Garbage Collection을 통해 메모리가 회수되는 영역이다.

 

Garbage Collector 종류

Garbage Collector Option Young Generation
Collection
Old Generation Collection
Serial Collector -XX: +UseSerialGC Serial Serial Mark-Sweep-Compact
Parallel Collector -XX: +UseParallelGC Parallel Scavenge Serial Mark-Sweep-Compact
Parallel Compacting Collector  -XX: +UseParallelOldGC Parallel Scavenge Serial Mark-Sweep-Compact
CMS Collector -XX: +UseConcMarkSweepGC Parallel Concurrent Mark-Sweep
G1 Collector -XX: +UseG1GC Snapshot-At-The-Beginning
Z Garbage Collector -XX:+UseZGC Mark-Relocate

Serial Collector 

  • Young / Old Generation 모두 Serial로 Single CPU를 사용한다.
  • 1개의 Thread를 가지고 GC를 수행한다.
  • stop-the-world 시간이 길다.
  • 메모리 사용량이 적은 소규모 응용 프로그램에 적합하다.
  • Client Class의 기본 Collector이며 현재 거의 사용되지 않는 collector이다.
  • Mark-sweep-compact 알고리즘을 사용한다.
 stop-the-world : GC를 실행하기 위해 JVM이 애플리케이션 실행을 멈추는 것, GC를 실행하는 스레드 외의 모든 스레드가 작업을 중단한다. 작업을 완료한 이후에 중단한 작업을 다시 시작한다.

Parallel Collector

  • Java 8의 default GC이다.
  • Young 영역에서의 컬렉션을 병렬(Parallel)로 처리한다.
  • 많은 CPU를 사용하는 반면에 GC의 부하를 줄이고 애플리케이션의 처리량을 증가시킬 수 있다. (Young Area에서만)
  • Serial GC보다 stop-the-world 시간이 짧다.
  • 같은 Memory 공간을 두 Thread가 접근하면 Corruption이 발생할 수 있지만 Hostpot JVM은 PLAB(Parallel Allocation Buffer)라는 Promotion Buffer를 마련하여 이런 Corruption을 회피한다.

Parallel Old Collector

  • Parallel GC를 개선한 버전이다.
  • Parallel Old Generation에서는 Young Area 뿐만 아니라 Old Area에서도 GC를 멀티 스레드로 수행한다.
  • Mark-Summary-Compact 알고리즘 사용한다.

 

Mark-Sweep-Compact

  1. Mark 단계 - 모든 Live Object들에 대한 Mark 작업을 하는 단계로서 참조 계수(Reference count)가 1 이상인 Object는 Mark 되며 나머지는 Garbage로 간주한다. Mark 된 Live Object는 Markbit vector에 주소가 저장된다.

2. Sweep 단계 - Mark 단계에서 Live Object의 주소 저장소인 Markbit vector와 모든 Allocation Object의 주소 저장소인 Allocbit vector와 교차 비교하여 Live Object 외 Garbage Object를 삭제하는 작업을 수행한다.

3. Compaction 단계 - Sweep 단계에서 Garbage Object들이 삭제되고 나면, Compaction 단계에서는 Heap 상의 삭제된 Garbage Object들의 공간에 대해 재정렬 작업을 수행한다. 이러한 Compaction 작업을 수행함으로써 연속된 Free Memory공간을 확보할 수 있다. Compaction 작업은 Object들이 Heap Memory 상에서 이동이 발생하며 이 경우 모든 Reference의 변경이 발생할 수 있어 수행 시간이 오래 걸린다.

 

Compation이 발생하는 경우

  • Sweep 단계 이후 Object할당을 위한 Free space가 충분하지 않을 경우
  • 가용한 Heap이 5% 이하일 경우
  • 가용한 Heap이 128kb 이하일 경우

CMS(Concurrent Mark Sweep) Collector

  • Heap 영역이 클 때 적합하다. (더 이상 일시 중지 시간을 감당할 수 없는 반응형 애플리케이션에 적합)
  • Suspend Time을 분산하여 응답 시간을 개선했고, 애플리케이션의 응답 속도가 매우 중요할 때 CMS GC를 사용한다.
  • 자원 여유가 있는 상태에서 GC의 Pause Time(stop-the-worild)을 줄이는 목적이며 Size가 큰 Long Lived Objecrt가 있는 경우 가장 적합하다.
  • Young Area에는 Parallel Copy 알고리즘, Old Area에는 Concurrent Mark-Sweep 알고리즘이 사용된다.
  • Floating Garbage 문제가 있다. 
  • JDK 9부터 이 GC 유형은 더 이상 사용되지 않는다. (G1 Collector의 등장)

 

Concurrent Mark-Sweep

 

  1. Initial Mark Phase - Single Thread만 사용하여 애플리케이션 이 중지되고 애플리케이션에서 직접 Reference 되는 Live Object만 구별한다. (Suspend 상태지만 속도가 빠르다.)
  2. Concurrent Mark Phase - Single Thread만 사용하여 애플리케이션은 수행되고 GC Thread 외 Working Thread는 애플리케이션 수행이 가능하다. Initial Mark Phase에서 선별된 Live Object가 Reference 하고 있는 Object를 추적해 Live 여부를 구별한다. stop-the-world 없이 해당 작업이 수행된다.
  3. Remark Mark Phase - Multi Thread를 사용하여  애플리케이션이 중지(stop-the-world)된다. 이미 Making 된 Object를 다시 추적, Live 여부 확정, 모든 Resource를 투입한다.
  4. Concurrent Sweep Phase - Single Thread만 사용하여 애플리케이션은 수행되고 최종 Live로 판면 된 Object를 제외한 Dead Object를 지운다. Single Thread만 사용하기 때문에 다른 Thread는 실행 중인 상태에서 stop-the-world 없이 해당 작업이 수행된다. 단 Sweep 작업만 하고 Compction 작업은 수행하지 않고 Free List를 사용하여 단편화를 줄이려고 노력한다. 

CMS Collector는 Old Area에 Object를 할당할 때 Free List를 사용해 단편화를 최소화하고 Scheduling으로 Remark 단계가 Minor GC 중간 지점에 오도록 조정하여 OOME(Out Of Memory Error)를 방지한다.

 

Free List란?

Promotion(승격) 할당을 할 때 Young Area에서 승격된 Object와 크기가 비슷한 Old Area의 Free Space를 Free List에서 탐색하게 된다. 이런 작업은 GC 수행 중 Young Area에 부담을 주게 되는데 그 이유는 Free List에서 적절한 Chunk 크기를 찾아 할당해야 하기 때문이고 시간도 오래 걸린다.(Eden/Survivor Area에 체류 시간이 길어진다.) Compaction 작업을 수행하면 Old Area에 올라온 Object를 그냥 순서대로 할당하여 Free Allocation이 가능한데 Compaction이 너무 고비용이라 손익을 따져봐야 한다. (승격이 빈번하지 않으면 성능 이득이 있다.)

 

Floating Garbage 

CMS collector에서는 Initial Mark 단계에서 Heap에 존재하던 객체만 GC의 수행 대상 여부를 판단하는 기준이 되어 Dead Object이지만 Cuncurrent Mark 단계에서 승격되어 Old영역에 존재하게 된 객체는 GC 대상에서 제외되어 있어 Floating Garbage라고 한다. Floating Garbage는 다음번 GC에서 사라진다.

 

Parallel Collector VS CMS Collector

Parallel GC 방식에서는 Full GC가 수행될 때마다 Compaction 작업을 진행하기 때문에 시간이 많이 소유된다. 하지만 Full GC가 수행된 이후에는 Memory를 연속적으로 지정할 수 있어 Memory를 더 빠르게 할당할 수 있다. 반대로 CMS GC는 Compaction 작업을 기본으로 수행하지 않기 때문에 속도가 빠르다. 하지만 Compaction 작업을 수행하지 않으면 디스크 조각 모음을 실행하기 전의 상태처럼 Memory에 빈 공간이 여기저기 생긴다. 그렇기 때문에 크기가 큰 Object가 들어갈 수 있는 공간이 없을 수 있다. (CMS GC를 사용할 때에는 Compaction 시간이 다른 Parallel GC보다 더 오래 소요된다.)

 

G1 Collector (Garbage First Collector)

  • CMS Collector에 비해 Puase Time이 개선되었고 예측이 가능하다.
  • G1 Collector는 물리적 Generation 구분을 없애고(Heap은 인접한 Young 및 Old 세대로 분할) 전체 Heap을 1 Mbytes 단위 Region으로 재편했다.
  • Garbage로 가득 찬 영역을 빠르게 회수하여 빈 공간을 확보하므로 GC 빈도가 줄어든다.
  • Java 9의 default GC이다.
  • 작은 Heap 공간을 가지는 Application에서는 제 성능을 발휘하지 못하고 Full GC가 발생한다.

Heap 영역

Minor GC가 발생하면 Young Area의 Region들을 대상으로 Reachable Object를 찾고 Age가 되지 않은 Object는 Survior Space에 Copy 한다. Promotion의 대상 Object는 Old Generation으로 Copy 한다. 기존 Young Generation Region은 Garbage로 간주해 Region 단위로 할당을 해지한다. Young Area GC가 끝나면 바로 Old Area GC를 시작하는데, 단 Heap 전체에 대한 GC는 아니며 철저히 Region 단위로 GC로 인한 Suspend 현상도 해당 Region을 사용하는 Thread에 국한된다.

 

Z Garbage Collector

  • 매우 큰(수 테라바이트) 힙에서 잘 작동하는 저지연 GC이다.
  • 정지 시간이 최대 10ms를 미만이다.
  • Heap의 크기가 증가하더라도 정지 시간이 증가하지 않는다.
  • 모든 종류의 고비용 작업을 동시(concurrently) 작업하고 이때 애플리케이션 스레드 실행을 중지되지 않는다.
  • 8MB~16TB에 이르는 다양한 범위의 Heap을 처리할 수 있다. 
  • 매우 짧은 일시 중지 시간이 필요한 대용량 메모리가 있는 응용 프로그램에 적합하다.
  • Java 11에서 확장된 저지연 GC이다.

정리
올바른 GC를 선택하는 것은 응용 프로그램의 요구 사항과 동작에 따라 크게 달라진다. 처리량, 대기 시간 및 공간을 기준으로 GC를 비교하여 애플리케이션에 가장 적합한 GC를 선택해야 한다.
반응형