Java

GC는 어떻게 이루어지는가?

shininghyunho 2024. 1. 29. 18:46

GC?

GC는 Garbage Collector다.

garbage = 참조되지 않는 객체

garbage를 청소함으로써 메모리 누수를 막고 메모리를 효율적으로 관리.

Garbage Collector는 Garbage Collection을 수행하는것이다.

 

Java는 C와 달리 메모리 관리를 직접하지 않는다.

JVM의 GC에서 대신 필요하지 않는 메모리를 정리해준다.

어떻게 설계되었는가?

간단히 생각해서 중간에 사용되지 않는 메모리를 체크해서 해제해주면 될것이다.

그러나 이 과정은 굉장히 자원을 많이 사용하는 비싼 작업이다.

그래서 GC는 최대한 적게하는것이 가장 중요하다.

 

객체들의 생명주기를 조사해보니 대부분의 객체들은 생명주기가 짧았다.

즉 대부분의 객체들이 금방 생성되고 사라지는것이다.

오래 살아남는 객체들은 소수다.

이를 약한 세대 가설(weak generational hypothesis)이라고 한다.

 

그래서 Heap 영역은 애기 세대인 Young Generation과 늙은이 Old Generation으로 나뉘어서 관리한다.

GC의 과정은 큰 틀에서 설명하자면

  • 주로 Young 에서 GC가 수행되다가
  • 나이가 먹으면 Old로 옮겨지고
  • Old도 꽉차면 그때 Old도 GC가 수행된다.

Young 에서 자주 일어나는 GC를 Minor GC라고 하고

Old에서 가끔 일어나는 GC를 Major GC라고 한다.

 

어떻게 동작하는가?

GC가 일어나려면 세상이 멈춰야한다. 정말 모든 과정을 멈춘다고해서

Stop The World라고 한다.

이때 GC를 수행하는 스레드를 제외한 모든 스레드를 멈춘다.

 

이후 Mark-Sweep-Compact 작업이 일어난다.

 

  • Mark : 사용중인 메모리와 사용중이지 않는 메모리를 선별
  • Sweep : 사용하지 않는 메모리를 해제
  • Compact : 디스크 조각모음처럼 메모리를 앞으로 당긴다.

 

구체적으로 살펴보자.

  • GC가 발생해서 사용되지 않는 객체는 Eden영역으로 온다.
  • Eden이 꽉차면 Survivor 1또는 2로 간다.
    이때 Survivor 1또는 2 중 한곳은 반드시 비어있어야한다.
  • Survivor 1이 꽉차면 다시 사용하지 않는 객체는 2로 이동한다.
    2에서도 꽉차면 다시 1으로 이동한다.
  • 이동하면서 각 age 비트를 두고 1씩 더한다. (나이을 먹어간다.)
  • JVM마다 설정한 age 임계점을 넘어가면 Old 영역으로 Promotion 시킨다. (이동한다)
  • 메모리가 부족하거나 Old가 꽉차면 GC가 발생해 Old 영역을 다시 체크한다.

여기서 Eden, Survivor 1,2에서 발생하는 GC를 Minor GC라고 하고

Old 에서 발생하는 GC를 Major GC라고 한다.

 

GC 종류

Serial GC(-XX:+UseSerialGC)

  • 싱글 스레드로 모든 종류의 가비지 컬렉션을 수행.
    • 적은 메모리와 CPU 코어 개수에서 적합.
  • Young Generation Collection 알고리즘: Serial
  • Old Generation Collection 알고리즘: Serial Mark-Sweep-Compact
  • Old 영역에서 살아남은 객체를 식별(Mark). 힙(Heap) 앞부분부터 살아남은것만 남김(Sweep). 객체들이 연속되어 쌓이도록 앞부분으로 떙김(Compaction) 이를 Mark-Sweep-Compact라고함
  • Young, Old 모두 싱글 스레드로 처림.

Parallel GC(-XX:+UseParallelGC)

  • 기본적으로 Serial GC와 같지만 Minor GC를 처리하는 스레드가 여러개다.
  • 메모리가 충분하고 코어 갯수가 많을 때 유리하다.
  • Java 8 의 Default GC 방식.
  • Young Generation Collection 알고리즘: Parallel Scavenge
  • Old Generation Collection 알고리즘: Serial Mark-Sweep-Compact
  • Young만 멀티 스레드로 처리.

Parallel Old GC(-XX:+UseParallelOldGC)

  • Parallel GC 에서 추가로 Old 영역 처리 방식을 멀티 스레드로 처리하는 Mark-Summary-Compact로 변경된다.
    • (구체적인 방법은 region 별로 나누어 병렬 처리한다는것만 이해)
  • Old 도 멀티 스레드로 처리.

CMS GC(-XX:+UseConcMarkSweepGC)

  • 전체 처리율보다 응답시간이 중요한 경우에 사용.
    • stop the world가 짧고
    • 다른 스레드가 실행중일때 동시에 진행이 됨.
  • 4가지 단계를 거침
    • Initial Mark : Root Set과 직접 관계가 있는 살아있는 객체만 마크.
      • stop the world
      • 싱글 스레드를 사용하지만 한 단계만 거치기 때문에 굉장히 빠름.
    • Concurrent Mark : GC 스레드와 Working 스레드가 동시에 작업.
      • GC 스레드는 싱글 스레드를 사용
      • 방금 체크한 객체들을 따라가며 살아있는지 마크.
    • Remark Phase : 마크한 객체들을 다시 추적해 살이있는지 확인.
      • stop the world
      • GC에 멀티 스레드 사용.
    • Concurrent Sweep Phase : 살아있는 객체를 제외하고 모두 삭제.
      • GC에 멀티 스레드 사용.
      • compaction(조각 모음)이 생략됨.
  • 다른 GC보다 메모리와 CPU를 더 많이 사용.
  • Compaction을 실행하면 조각난 메모리가 더 많아 stop the world가 오래걸림.

G1 GC(Garbage-First Garbage Collector)

  • Heap 을 Region으로 구분하여 각 Region에 대해서 GC가 발생함.
  • 다른 GC들 보다 빠르다.
      •  

참고

Java 의 GC는 어떻게 동작하나? - J's log (mirinae312.github.io)

JVM 구조와 JAVA의 동작 원리 (velog.io)