GC 정리

띠용·2025년 12월 15일

우테코 7기 BE

목록 보기
12/12

1. GC 정의

Garbage Collection : 더 이상 다른 객체에 의해 참조되지 않는 객체를 찾아 메모리를 해제하는 작업


2. GC 장단점

장점

  • 개발자의 실수로 인한 메모리 관련 오류 방지;

단점

  • GC 실행 오버헤드(STW) 발생
  • 개발자가 메모리 해제 시점을 정확히 예측하기 어려움

3. GC 동작 방식

참조가 끊어진 객체를 찾아내는 방법

3.1. 참조 카운팅(Reference Counting)

각 객체는 자신을 참조하는 변수의 수를 나타내는 참조 카운트를 가짐

  • 새로운 참조가 생길 때마다 카운트가 1 증가. 해제될 때마다 1 감소.
  • 카운트가 0이 되면 즉시 메모리에서 해제

순환 참조 문제가 있음

  • A가 B를 참조하고, B가 A를 참조하는 경우 서로 참조 카운트가 1에서 줄지 않음.
  • 메모리 누수

3.2. 마크 앤 스윕 (Mark-and-Sweep)

순환 참조 문제를 해결하기 위해 고안.

  1. Mark : GC 루트 스페이스로부터 그래프 순회 방식으로 연결된 모든 객체를 찾아 살아있는 객체(Reachable)로 표시함. 이때 루트는 스택 변수(참조 변수), 전역 변수 등 항상 유효하다고 보장되는 참조 집합.
  2. Sweep : Heap 메모리 전체를 스캔하여 Mark 되지 않는 Unreachable 객체를 메모리에서 제거함.(Sweep). 순환 참조 관계에 있는 객체들도 루트에서 도달할 수 없다면 수거된다.
  • Compaction : Sweep 이후 메모리 공간이 조각나는 *메모리 단편화 현상을 해결하기 위해 살아남은 객체들을 한쪽으로 모아 메모리 공간을 연속적으로 만든다.
    • 메모리 단편화 현상
      • 이미 사용 중인 메모리가 힙의 중간 중간 떨어져 있으면 특정 크기의 메모리를 할당할 때 매번 적합한 크기의 빈 공간을 찾는 비용이 발생한다.
      • 이미 사용 중인 메모리가 힙 한쪽에 몰려있으면 나머지 공간이 연속된 빈 공간이 된다. 따라서 새로 객체가 필요할 때 연속된 빈 공간의 시작점 주소만 알면 된다.

STW (Stop The Wolrd) : 마킹이 이루어지는 동안 모든 애플리케이션 스레드를 중지 시켜서 그래프의 변경을 막는 작업.

  • Concurrent Marking GC(CMS, G1, Shenandoah, ZGC)의 경우, 마킹의 대부분이 STW없이 동시에 수행하지만 Initial Mark/Remark 등의 단계는 STW로 수행한다.

4. JVM GC

4.1. JVM 루트 스페이스

JVM GC는 마크 앤 스윕 알고리즘을 기반으로 동작한다.

  • JVM GC의 루트 스페이스는 다음과 같다.
    • 스택 영역 : 각 스레드의 스택에 존재하는 로컬 변수 및 파라미터
    • 메소드 영역 : 클래스의 스태틱 변수
    • JNI : C/C++ 등 네이티브 코드를 통해 생성된 객체에 대한 참조

4.2. 세대별 GC 영역

JVM GC는 “대부분의 객체가 생성된 직후에 참조가 끊기며, 오래 살아남는 객체는 드물다”는 세대별 가설에 기반한다. 따라서 JVM은 힙 영역을 객체의 생애 주기에 따라 두 영역으로 나누어 관리한다.

  • Young Generation : 새롭게 할당된 객체들이 할당되는 공간. 마이너 GC가 발생한다.
    • Eden : 새로 생성된 객체가 최초로 할당되는 영역
    • Survivor 0 / 1 : 마이너 GC에서 살아 남은 객체들이 이동하는 영역. 두 영역 중 하나는 비어있어야 함.
      • 복사 수거(Copying Collector) GC로 메모리 단편화 방지
      • 루트로부터 참조되는 객체를 바로 반대 Survivor 영역으로 복사함.
  • Old Generation : Young Generation 에서 오래 살아남은 객체들이 승격하여 이동하는 공간.
    • Old Gen에서만 발생하는 GC를 Major GC라고 부르며, GC 알고리즘이나 상황에 따라 Young/Meta space까지 포함하는 Full GC로 확장될 수 있다.

4.3. 동작

  1. 새로운 객체가 생성되면 YG의 Eden 영역에 할당된다.

  2. Eden이 가득 차면 마이너 GC가 발생한다.

    살아남은 객체들은 Survivor(0/1) 영역으로 이동하면서 Age가 1 증가한다.

  3. 마이너 GC가 반복될 때마다 두 Survivor 영역을 오가며 Age 값이 증가한다.

    이때 임계값(Java 8 Parallel GC 기본값 15)에 도달한 객체는 Old Generation으로 승격(Promotion)된다.

  4. OG영역까지 가득 차면 메이저 GC가 발생한다. 훨씬 크고 밀집된 OG 영역에서 작동하기 때문에 STW 시간이 길고, 애플리케이션 성능에 큰 영향을 미친다. 따라서 메이저 GC의 빈도와 실행시간을 줄이는 것이 GC 튜닝의 핵심 목표이다.


5. JVM GC 알고리즘

자바가 발전함에 따라 최적화를 위한 다양한 GC 알고리즘이 개발되었다. 이러한 알고리즘은 설정을 통해 적용할 수 있다. 즉, 상황에 따라 필요한 GC 알고리즘을 선택할 수 있다.

5.1. Serial GC

CPU 코어가 1개일 때 사용하기 위해 개발된 단순한 GC. 가장 STW 시간이 길다.

마이너 GC에는 복사 수거(Copy Collector), 메이저 GC에는 Mark-Sweep-Compact를 사용한다.

보통 사용하지 않는다.

  • 실행 명령어
    java -XX:+UseSerialGC -jar Application.java

5.2. Parallel GC

Java 8의 기본 GC

마이너/메이저 GC 작업을 멀티 스레드로 병렬 처리하여 멀티 코어에서 GC 처리량을 극대화. Java 6/7 때는 마이너 GC만 병렬 처리했는데 8 이후로는 메이저도 병렬 처리가 기본임.

  • 실행 명령어
    java -XX:+UseParallelGC -jar Application.java 
    # -XX:ParallelGCThreads=N : 사용할 쓰레드의 갯수

5.3. CMS(Concurrnet Mark Sweep) GC

STW 시간을 최소화하는 데 초점을 맞춘 알고리즘. Mark-Sweep 작업을 애플리케이션과 동시에 수행하여 STW가 발생하는 구간을 줄임.

Compaction을 안하니까 메모리 파편화 / GC 과정 복잡함/ 높은 CPU 사용량 등의 이유로 자바 14부터 제거됨.

5.4. G1 GC

Java 9 이상부터 기본 GC

기존 Young/Old Generation 구분과 달리 전체 힙 영역을 여러 개의 일정한 Region으로 구분한다. 각 리전은 상황에 따라 동적으로 Eden, Survivor, Old 역할을 수행한다.

수 많은 리전 중에서 가장 많은 Garbage를 포함하는 영역을 우선적으로 수거(Garbage First)한다.

수거할 Garbage가 많을수록 GC 시간 대비 확보하는 메모리 양이 높아진다.

  • 한번에 힙 전체가 아니라 가장 효율적인 영역만 선택적으로 처리한다.
  • 살아남은 객체는 다른 Survivor 또는 Old Region으로 이동한다.
  • 실행 명령어
    java -XX:+UseG1GC -jar Application.java

처리량과 저지연의 밸런스를 지향한다. (균형형)

  • Concurrent GC(Shenandoah/ZGC 등)는 STW 시간이 줄지만, GC 스레드가 CPU를 사용하기 때문에 애플리케이션이 쓸 CPU가 줄어 처리량이 줄 수 있다.

5.5 Shenandoah GC

java 12에 release

G1처럼 Region 기반인데 CMS보다 더 나아가 동시 Compaction까지 수행하여 Full GC로 인한 긴 STW를 피하는 설계.

  • 실행 명령어
    java -XX:+UseShenandoahGC -jar Application.java

5.6 ZGC

java 15에 release

대량 메모리를 저지연으로 처리하기 위해 디자인된 GC

G1의 Region처럼, ZGC는 ZPage라는 영역을 사용하는데 ZPage 크기는 동적으로 운영된다.

G1에 비해 저지연을 지향, (처리량을 손해 보더라도 지연 시간 최소화)

Shenandoah와 마찬가지로 동시 Compaction을 지향하는 저지연 GC 지만, 참조 처리 방식과 내부 알고리즘이 다르다.

  • 실행 명령어
    java -XX:+UnlockExperimentalVMOptions -XX:+UseZGC -jar Application.java

참고

https://inpa.tistory.com/entry/JAVA-%E2%98%95-%EA%B0%80%EB%B9%84%EC%A7%80-%EC%BB%AC%EB%A0%89%EC%85%98GC-%EB%8F%99%EC%9E%91-%EC%9B%90%EB%A6%AC-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%F0%9F%92%AF-%EC%B4%9D%EC%A0%95%EB%A6%AC

https://youtu.be/qmVtSGUrQ04?si=I_FFBcR-2RJPOYRa

0개의 댓글