Garbage Collection(GC)

YH·2023년 3월 27일
0

✅ Garbage Collection(GC) 란?

  • 자바의 메모리 관리 방법 중의 하나로, JVM의 Heap 영역에서 동적으로 할당했던 메모리 중 필요없게 된 메모리 객체를 모아 주기적으로 제거하는 프로세스를 말함

✅ GC의 장점 & 단점

✔️ 장점
1. GC가 메모리 관리를 대신해주기 때문에 개발자 입장에서 메모리 관리나 메모리 누수(Memory Leak)에 대해서 관리하지 않아도 됨

✔️ 단점
1. 개발자 입장에서 메모리가 언제 해제되는지 정확하게 알 수 없어 제어하기 힘듦
2. GC가 동작하는 동안 다른 동작이 일시 정지되기 때문에 오버헤드가 발생함, 이를 전문 용어로 Stop-The-World(STW) 라고 함

Stop-The-World
-> GC를 수행하기 위해 JVM이 프로그램 실행을 멈추는 현상

✅ GC의 대상

  • GC는 특정 객체가 Garbage인지 판단하기 위해 Reachability(도달성) 라는 개념을 사용
  • 객체에 참조가 남아있다면 Reachable, 참조가 없다면 Unreachable로 구분하여 제거 함
  • Heap 영역의 객체들이 메소드가 끝난 후 객체의 메모리 주소를 가지고 있는 참조 변수가 스택이나 메소드 영역에서 제거되면, Heap 영역의 객체는 Unreachable 상태가 되며, 해당 객체를 GC에서 제거하게 됨

✅ GC의 동작 방식

Mark And Sweep

  • Mark And Sweep은 여러 GC에서 사용되는 객체를 솎아내는 내부 알고리즘
  • 기초적인 동작 방식
  • Garbage 대상 객체를 식별(Mark)제거(Sweep)하며, 객체가 제거되고 파편화 된 메모리 영역을 앞에서부터 채워나가는 작업(Compaction)을 수행
    • Mark : Root Space로부터 사용중인 메모리(객체)와 사용되지 않는 메모리를 식별하는 작업
      • Root Space는 Heap 영역을 참조하고 있는 Method Area, static 변수, native method stakc이 대상이 된다.
    • Sweep : 식별 후 사용되고 있지 않은 메모리(객체)를 Heap에서 제거
    • Compact : Sweep 후 분산된 객체들을 Heap의 시작 주소로 모아 메모리가 할당된 부분과 할당되지 않은 부분으로 압축한다. (GC 종류에 따라 하지 않는 경우도 있음)

✅ Heap 메모리 구조

✔️ Heap은 동적으로 참조 객체가 저장되는 공간으로, GC의 대상이 됨
✔️ Heap 영역은 효율적인 메모리 관리를 위해 Young GenerationOld Generation 으로 나뉨
✔️ Permanent Generation도 있었으나, JAVA 8 버전 이후로 제거

  • Young Generation
    ✔️ 새롭게 생성된 객체가 할당되는 영역
    ✔️ 대부분의 객체가 금방 Unreachable 상태가 되기 때문에, 많은 객체가 Young 영역에 생성되었다가 사라짐
    ✔️ Young 영역에 대한 GC를 Minor GC라고 부름
  • Old Generation
    ✔️ Young 영역에서 Reachable 상태를 유지하여 살아남은 객체가 복사되는 영역
    ✔️ Young 영역보다 크게 할당되며, 영역의 크기가 큰 만큼 Garbage는 적게 발생
    ✔️ Old 영역에 대한 GC를 Major GC 또는 Full GC라고 부름
    ✔️ Old 영역에는 카드 테이블이라는게 존재하는데, 카드 테이블은 Old 영역의 객체가 young 영역의 개체를 참조할 때 마다 그에 대한 정보가 표시 됨. Minor GC가 수행될 때 해당 카드 테이블을 조회하여 GC인지 식별하도록 하는데, 즉 모든 Old 영역의 객체를 조회할 필요가 없다는 이점이 있다.

Old 영역이 Yound 영역보다 크게 할당되는 이유는 Young 영역의 수명이 짧은 객체들은 큰 공간을 필요로 하지 않으며, 큰 객체들은 Old 영역으로 할당되기 때문이다.

✅ Minor GC 동작 방식

Young 영역의 구조

✔️ Young 영역은 3가지의 영역으로 나뉨,

  • Eden
    • 새로 생성된 객체가 할당되는 영역
  • Survival 0 / Survival 1
    • 최소 1번의 GC 이상 살아남은 객체가 존재하는 영역
    • Suvival 0 또는 Suvivor 1 중에 하나에는 꼭 비어 있어야 하며 & 남은 하나에는 무조건 사용되어야 한다.

동작 방식

  1. 새로 생성된 객체가 Young 영역 중 Eden 영역에 할당됨
  2. 객체가 계속 생성되어 Eden 영역이 꽉차게 되면 Minor GC가 실행 됨
    2-1. Eden 영역에서 사용되지 않는 객체는 제거됨
    2-2. Eden 영역에서 살아남은 객체는 Survivor 영역으로 이동 됨
  3. 살아남은 객체는 age값이 1 증가
  4. 1~3번 과정을 반복하다가 Survivor 영역이 다 차게되면 Suvivor 영역의 살아남은 객체를 다른 Survivor 영역으로 이동시킨다.
  5. 이러한 과정을 계속 반복하여 살아남은 객체는 Old 영역으로 이동(Promotion)

🔍 age 값이란,
Minor GC에서 객체가 살아남은 횟수를 의미하며 Object Header에 기록 됨
JVM 중 가장 일반적인 HotSpot JVM의 경우 이 age의 기본 임계값은 31
Object Header에 age를 기록하는 부분이 6 bit로 되어 있기 때문

🔍 HotSpot JVM에서는 Eden 영역에 객체를 빠르게 할당(Allocation)하기 위해 bump the pointerTLABs(Thread-Local Allocation Buffers)라는 기술을 사용

  • bump the pointer란, Eden 영역에 마지막으로 할당된 객체의 주소를 캐싱해두는 것이다. bump the pointer를 통해 마지막 주소의 다음 주소를 사용하게 함으로써 속도를 높일 수있다. 이를 통해 새로운 객체를 할당할 때 객체의 크기가 Eden 영역에 적합한지만 판별하면 되므로 빠르게 메모리 할당을 할 수 있다.
  • TLABs(Thread-Local Allocation Buffers)란, 각각의 쓰레드마다 Eden 영역에 객체를 할당하기 위한 주소를 부여함으로써 동기화 작업 없이 빠르게 메모리를 할당하도록 하는 기술이다. 각각의 쓰레드는 자신이 갖는 주소에만 객체를 할당함으로써 동기화 없이 bump the poitner를 통해 빠르게 객체를 할 수 있다.

✅ Major GC 동작 방식

  1. 객체의 age 값이 임계값에 도달하면, 해당 객체는 Old 영역으로 이동(Promotion) 됨
  2. 위 과정이 반복되어 Old 영역의 공간이 다 차게되면, Major GC가 실행 됨
  • Major GC는 Old 영역이 상대적으로 크고, 반대로 Young 영역을 참조할 수도 있기 때문에 처리 시간이 Minor GC에 비해 10배이상 오래 걸린다.

✅ GC Algorithm

🔸GC 알고리즘은 GC가 실행되며 발생하는 Start-The-World 문제를 해결하기 위해 여러 알고리즘이 고안됨

🗒️알고리즘 종류

  ① Serial GC
  ② Parallel GC
  ③ Parallel Old GC
  ④ CMS(Concurrent Mark Sweep) GC
  ⑤ G1(Garbage First) GC
  ⑥ Shenandoah GC
  ⑦ ZGC(Z Garbage Collector)

① Serial GC

✔️ 서버의 CPU가 1개일 때 사용하기 위해 개발된 가장 단순한 GC
✔️ GC를 처리하는 쓰레드가 1개이므로, Stop-The-World 시간이 길다
✔️ Minor GC에서는 Mark-Sweep, Major GC에서는 Mark-Sweep-Compact 기술을 사용
✔️ 실무 환경에서 쓰이는 일은 거의 없음

⬇️ 실행 명령어

java -XX:+UseSerialGC -jar Application.java

② Parallel GC

✔️ Java 8의 기본 GC
✔️ 기본적인 처리 과정은 Serial GC와 동일
✔️ Young 영역의 Minor GC를 멀티 쓰레드로 수행(Old 영역은 싱글 쓰레드)
✔️ Serial GC에 비해 오버헤드를 줄여 Stop-The-World 시간 감소

⬇️ 실행 명령어

java -XX:+UseParallelGC -jar Application.java 
# -XX:ParallelGCThreads=N : 사용할 쓰레드의 갯수
# -XX:MaxGCPauseMillis=<N> : 최대 지연 시간

③ Parallel Old GC

✔️ Parallel GC를 개선한 버전, JDK5 Update6 부터 제공
✔️ Mark-Summary-Compaction 기술을 사용
✔️ Summary 단계에서 앞서 GC를 수행한 영역에 대해서 별도로 살아있는 객체를 색별
✔️ Old 영역 또한 멀티쓰레드로 처리

⬇️ 실행 명령어

java -XX:+UseParallelOldGC -jar Application.java
# -XX:ParallelGCThreads=N : 사용할 쓰레드의 갯수

④ CMS(Concurrent Mark Sweep) GC

✔️ 어플리케이션 쓰레드와 GC 쓰레드를 Concurrent하게 실행
✔️ GC 과정이 복잡하며, 메모리와 CPU를 많이 필요로 함
✔️ Compaction 단계를 수행하지 않아 메모리 파편화 문제가 발생
✔️ Java9 버전부터 deprecated, Java14에서는 사용 중지

⬇️ 실행 명령어

# deprecated in java9 and finally dropped in java14
java -XX:+UseConcMarkSweepGC -jar Application.java

⑤ G1(Garbage First) GC

✔️ Java7 버전부터 지원되기 시작한 CMS GC를 대체하기 위한 알고리즘
✔️ Java 9+ 버전의 기본 GC
✔️ 4GB 이상의 힙 메모리, Stop the World 시간이 0.5초 정도 필요한 상황에 사용 (Heap이 너무작을경우 미사용 권장)
✔️ 기존에 Young 영역과 Old 영역을 나누어 사용한 방식과 달리, Region이라는 개념을 도입하여 Heap을 균등하게 여러 지역으로 나누고, 각 지역을 역할과 함께 논리적으로 구분(Eden, Survivor, Old)하여 객체를 동적으로 할당함
✔️ Garbage가 많은 Region에 대해 우선적으로 GC를 수행하는 것이 핵심
✔️ HumongousAvailabe/Unused 라는 역할이 추가됨
  🔸Humongous : Region 크기의 50%를 초과하는 객체를 저장하는 Region
  🔸Availabe/Unused : 사용되지 않는 Region
✔️ Minor GCMajor GC에 대한 동작이 아래와 같이 나뉘어 수행 됨

  • Minor GC
    ✒️ 한 지역에 객체를 할당하다가 해당 지역이 꽉 차면 다른 지역에 객체를 할당하고 Minor GC가 실행, Garbage가 가장 많은(Garbage First) 지역을 찾아서 Mark-Sweep을 수행.
    ✒️ Eden 지역에서 GC가 수행되면 살아남은 객체를 식별(Mark) 및 메모리 회수(Sweep) 후, 다른 지역으로 이동시킴
    ✒️ 이동되는 지역이 Available/Unused 지역이면 해당 지역은 Survivor 영역이 되고, Eden 영역은 Available/Unused 지역이 됨

  • Major GC
    ✒️ 시스템 운영 중에 객체가 많아져 빠르게 메모리 회수가 어려울 때 Major GC가 실행됨
    ✒️ 기존 GC 알고리즘에서는 모든 Heap 영역에서 GC가 수행 되었으나, G1 GC는 Garbage가 많은 영역을 알고있기 때문에 해당 지역에 대해서만 GC를 수행함
    ✒️ 위 작업 또한 Concurrent 하게 수행되므로 어플리케이션의 지연도 최소화 됨

⬇️ 실행 명령어

java -XX:+UseG1GC -jar Application.java

⑥ Shenandoah GC

✔️ Java12 부터 지원됨
✔️ 래드 헷에서 개발한 GC
✔️ 기존 CMS가 가진 단편화, G1이 가진 pause의 이슈를 해결
✔️ 강력한 Concurrency와 가벼운 GC 로직으로 heap 사이즈에 영향을 받지 않고 일정한 pause 시간이 소요가 특징

⬇️ 실행 명령어

java -XX:+UseShenandoahGC -jar Application.java

⑦ ZGC(Z Garbage Collector)

✔️ Java15 부터 지원됨
✔️ 대량의 메모리(8MB ~ 16TB)를 low-latency로 잘 처리하기 위해 디자인 된 GC
✔️ G1의 Region 처럼, ZGC는 ZPage라는 영역을 사용하며, G1의 Region은 크기가 고정인데 비해, ZPage는 2mb 배수로 동적으로 운영됨. (큰 객체가 들어오면 2^ 로 영역을 구성해서 처리)
✔️ 힙 크기가 증가하더도 'stop-the-world'의 시간이 절대 10ms를 넘지 않음

⬇️ 실행 명령어

java -XX:+UnlockExperimentalVMOptions -XX:+UseZGC -jar Application.java

참조 Reference

profile
하루하루 꾸준히 포기하지 말고

0개의 댓글