[Java] GC 동작 원리

Kim Hyen Su·2024년 6월 4일
0

📐Java

목록 보기
13/18
post-thumbnail

GC(Garbage Collection)

🔵 가비지 컬렉션 이란?

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

이는 C 언어와 다르게, Java에서는 가비지 컬렉터가 메모리 관리를 자동으로 수행해줍니다. 이로 인해 Java는 메모리를 효율적으로 사용할 수 있으며, 개발자는 메모리 누수, 관리하지 않아도 비지니스 로직 개발에만 집중할 수 있습니다.

가비지 컬렉션의 단점도 물론 존재합니다.
자동으로 처리해주지만, 언제 처리되는지 정확한 시점을 알 수 없어 제어가 어렵습니다. 또한, GC 작업 동안에 "stop the world"로 인해서 다른 동작은 멈춰버림으로써 발생하는 오버헤드 존재합니다.

가비지 컬렉션 대상

가비지 컬렉션은 참조되는 객체를 "Reachable", 참조되지 않는 객체를 "Unreachable"로 구분하여 처리합니다.

가비지 컬렉션 기본 방식

이는 Mark와 Sweep 알고리즘을 통해서 사용되지 않는 객체를 찾아냅니다. 즉, 가비지 컬렉션이 될 대상 객체를 식별(Mark) 후 제거(Sweep)하며, 객체가 제거됨으로써 파편화된 메모리 영역을 앞에서부터 채워 압축(Compaction)하는 작업을 수행합니다.

Compaction 과정은 가비지 컬렉션 종류에 따라서 수행하지 않는 경우도 있습니다.


🔵 가비지 컬렉션 동작 과정

JVM의 Heap 영역은 동적으로 할당되는 공간으로서, 가비지 컬렉션 대상이 되는 영역입니다.

Heap 영역은 Weak Generational Hypothesis를 기반으로 설계되었습니다.

📜 Weak Generational Hypothesis

  1. 대부분의 객체는 단기간 동안만 생존한다.(young generation)
  2. 일부 객체만이 장기간 생존한다.(old generation)

따라서, 객체의 대부분은 짧은 시간 내에 참조를 잃게 되며, 오랫동안 남아있는 경우는 드물다는 가설입니다.

이러한 가설을 기반으로 JVM의 메모리를 보다 효율적으로 관리하기 위해서 객체의 생존기간에 따라 물리적으로 Heap 영역을 구분하게 되었고 크게 Young과 Old 영역으로 구분됩니다.

Young 영역

새롭게 생성된 객체에 메모리를 할당하는 영역입니다. 대부분 객체가 금방 Unreachable 상태가 되기 때문에, 많은 객체가 Young 영역에 생성되었다가 사라집니다.

Young 영역 내에서 발생한 GC를 Minor GC라고 합니다.

Old 영역

Young 영역에서 Reachable 상태를 유지하며 살아남은 객체가 복사되는 영역을 말합니다. Young 영역보다 공간이 넓으며, 그만큼 적은 GC가 발생합니다.

Old 영역에서 발생한 GC를 Major GC 또는 Full GC라고 합니다.

Eden, Survivor0. Survivor2

Heap 메모리 내에서 조금 더 효율적인 관리를 위해서 Young 영역 을 3가지 영역으로 나눴습니다.

  • Eden : new를 통해서 새로 생성된 객체 할당 공간

  • Survivor 0/ Survivor 1 : 최소 1번 이상 살아남은 객체의 할당 공간으로, 두 공간 중 하나는 반드시 비어있어야 합니다.

📜 Java 8 에서의 Permanent 영역

일반적으로 생성된 객체들 관련 주소값이 저장된 공간을 말합니다. 클래스 및 메서드 등에 대한 MetaData가 저장되는 영역입니다. Java 7까지는 Heap 영역 내 존재했지만, Java 8 이후부터 Native Method Stack 에 편입되었습니다.

Minor GC

Minor GC는 Young 영역 내에서 GC를 수행하며, Young 영역은 상대적으로 공간이 작으므로 메모리 상 객체를 찾아 제거하는데 적은 시간이 걸립니다.

  1. 처음 생성된 객체는 Eden 영역에 위치합니다.

  2. 객체가 생성되면서 Eden 영역이 Full로 차게되고 Minor GC가 수행됩니다.

  3. Mark 동작을 통해서 reachable 객체를 탐색합니다.

  4. Eden 영역에서 살아남은 객체는 둘 중 하나의 Survivor 영역으로 이동됩니다.

  5. Eden 영역에서 unreachable 객체를 Sweep(메모리 해제)합니다.

  6. 살아남은 객체들은 age가 1씩 증가합니다.

📜 age란?

Survivor 영역 내 객체가 살아남은 횟수를 의미하며, Object Header에 기록됩니다. age를 기록하는 부분이 6bit로 되어 있습니다.

  1. 1~3 단계 반복한 뒤 살아남은 객체를 Survivor 영역으로 이동 시 비어있는 Survivor 영역으로 이동하게 됩니다.

  2. 이 후에 Eden 영역과 기존에 객체가 있던 Survivor 영역에 객체들을 Sweep합니다.

  3. 6번 단계를 수행하며, 이러한 과정을 반복합니다.

Major GC

Old 영역 내에서 수행되는 GC를 말합니다.

Old 영역 내 객체들은 Young 영역에서 age 임계치에 도달한 객체들이 복사되어 저장된 객체들입니다.

Major GC는 Old 영역 메모리가 꽉 찬 경우 수행됩니다.

  1. 객체의 age가 임계치를 도달하게 되면 해당 객체들은 Old 영역으로 복사되며, 이는 promotion 이라고 합니다.

  2. 1번 과정이 반복되다가 Old 영역 내 메모리가 부족하게 되면 Major GC가 발생합니다.

Major GC가 발생 시 Thread가 멈추가 Mark and Sweep 작업을 수행하게 됩니다.(stop the world) 이는 CPU에 부담이 되며, 이와 같은 시간 지연을 최대한 줄이는 것이 GC 튜닝입니다.

🔵 GC 알고리즘 종류

JVM이 자동으로 메모리를 관리해주는 것은 좋지만, Major GC 수행 시마다 stop the world가 발생되고 이로 인해 애플리케이션이 중지되는 문제가 발생하게 됩니다. 이를 최적화하기 위해 다양한 방식의 GC 알고리즘이 개발되었습니다.

Serial GC

  • 서버의 CPU 코어가 1개일 때 사용하기 위한 단순한 방식의 GC
  • GC를 처리하는 쓰레드가 1개 이므로, 처리 시간이 가장 깁니다.
  • 이는 Minor에서 Mark-Sweep, Major에서 Mark-Sweep-Compaction을 수행합니다.
  • 보통 실무에서 사용하는 경우는 없습니다.

Parallel GC

  • Java 8부터 default로 사용되는 GC입니다.
  • Serial과 기본적인 알고리즘은 같지만, Young 영역에서 Minor GC를 멀티쓰레드 방식으로 수행합니다.
  • 기존의 Serial 방식보다 상대적으로 처리 시간이 빠릅니다.

Parallel Old GC

  • Parallel GC를 개선한 방식으로, Old 영역에서도 멀티 쓰레드로 GC를 수행합니다.
  • 새로운 가비지 컬렉션 방식인 Mark-Summary-Compact 방식을 이용합니다.

CMS GC(Concurrent Mark Sweep)

  • 어플리케이션의 쓰레드와 GC 쓰레드가 동시에 실행되어 stop the world 시간을 최대한 줄이기 위해서 고안된 GC
  • 단, GC 과정이 매우 복잡해지며, 이로 인해 CPU 사용량이 상대적으로 많습니다.
  • 또한, 메모리 파편화 문제가 발생됨으로써 Java 9 버전 시 deprecated, Java 14 버전부터 사용이 중지된 GC입니다.

G1GC(Garbage First)

  • Java 9+ 버전의 default GC로 지정되었으며, Heap 메모리가 너무 작을 경우 미사용이 권장됩니다.(최소 4GB 이상)
  • 기존의 GC 알고리즘에서는 Heap 영역을 물리적으로 고정된 Young/Old 영역으로 나누어 사용하였지만, G1GC는 아예 이러한 개념을 뒤엎는 Region 이라는 개념을 새로 도입하여 사용하였습니다.
  • Heap의 전체 영역을 Region 이라는 영역으로 체스판 같이 분할하여 상황에 따라 Eden, Survivor, Old 영역으로 동적으로 부여해줍니다.
  • 메모리가 많이 차있는 region을 인식하여 많이 차있는 region 부터 GC를 수행합니다.
  • 전체 메모리를 탐색하는 것에서 region별로 나눠 탐색하며 GC가 수행되기 때문에 효율적입니다. 또한, 기존의 GC처럼 Eden, Survivor 영역으로 옮겨가며 GC를 수행했지만, G1GC는 효율적이라고 생각되는 위치를 내부연산하여 재할당(Relocate)시켜줍니다.

Schenandoah GC

  • Java 12부터 release 된 GC입니다.
  • RedHat에서 개발한 GC로, 기존의 CMS GC가 가지고 있던 단편화 문제와, G1GC가 가지고 있는 pause 이슈를 해결한 GC입니다.

ZGC(Z Garbage Collector)

  • Java 15부터 release 된 GC입니다.
  • 대량의 메모리를 low-latency로 잘처리하기 위해 디자인 된 GC입니다.
  • G1GC 처럼 ZGC 만의 영역인 ZPage를 사용하며, Zpage의 경우 2배수로 동적으로 할당됩니다.
  • ZGC의 장점은 힙의 크기가 아무리 증가하더라도, stop the world 시간이 10ms를 초과하지 않는다는 점입니다.

💡 GC 알고리즘 관련 조금씩 이해해보기

profile
백엔드 서버 엔지니어

0개의 댓글