Introduction to Java GC

by fienestar

메모리 관리?

var a = new Integer(10);
var b = a;
System.out.println(a);

a = null; // 아직 객체는 참조되는중
b = null; // 더 이상 해당 객체는 참조되지 않음

Strategies

탈출 분석

컴파일 타임에 언제 삭제되는지 아는 경우 그 위치에 메모리 해제 코드를 삽입

참조 카운팅

{
    A a = new A; // ++
} // --

참조중인 변수의 lifecycle이 종료되는 시점에 참조카운트--, 참조하기 시작할때 ++
카운트가 0이 되면, 자원 해제

참조 카운팅

// ref_count는 컴파일러에 의해 생긴 코드
public void f(A a){
    a.ref_count += 1;
    // use a
    a.ref_count -= 1;

    if(a.ref_count == 0) remove(a)
}
public static void main(String[] args) {
    A a; // default value of A::ref_count = 1
    f(a)
    a.ref_count -= 1;
    if(a.ref_count == 0) remove(a)
}

참조 카운팅

  • 순환 참조 문제

    b.m = a
    a.m = b
    
  • 동시성 문제

    • ref_count에 대한 증감시 임계 영역을 구성하거나 atomic operation 으로 수행해야함
  • 공간 비용

추적

  • 더 이상 참조되지 않는 메모리를 찾음
  • 우리가 다룰 방식(GC)

다루지 않는 것

  • 특정 GC의 자세한 구현
  • 메모리 할당 알고리즘
  • 이제는 사용되지 않는 GC들

다루는 것

  • GC를 쓰는 이유
  • GC의 기본 로직
  • 각 GC는 언제 써야할까?

기준

  • Java 18

Garbage Collection

  • 도달할 수 있는 객체를 live object라 함
  • 도달할 수 없는 객체를 garbage라 함




도달할 수 있다?

어떤 스레드에 의해 직/간접적으로 참조되고 있다

Garbage Collection

  • 도달할 수 있는 객체를 live object라 함
  • 도달할 수 없는 객체를 garbage라 함
  • 쓰레기를 수집하는 행위를 Garbage Collection(GC)이라 함
  • 쓰레기를 찾는 알고리즘(방식)이나, 수행하는 객체 Garbage Collector 또한 GC라고 부름

전제

  • 오래된 객체에서 젊은 객체로의 참조는 아주 적게 존재한다.
  • 대부분의 객체는 금방 garbage가 된다.

세대

  • Young
    새로 생성한 대부분의 객체
    이 영역에서의 gc를 Minor GC라 하고 자주 수행된다.
  • Old
    오랫동안 생존한 객체.
    이 영역에서의 gc를 Major GC라 하고 드물게 수행된다.

세대

  • Young은 Eden과 두개의 Survivor(from, to)로 구성된다.
// low                                                                    high
//
//                                        +-- generation boundary
//                                        |   (fixed after startup)
// |<- Young gen (reserved MaxNewSize)  ->|<- Old gen (reserved MaxOldSize) ->|
// +-----------+--------+--------+--------+---------------+-------------------+
// | eden      |  from  |   to   |        |      old      |                   |
// |           |  (to)  | (from) |        |               |                   |
// +-----------+--------+--------+--------+---------------+-------------------+
// |<-       committed         ->|        |<- committed ->|

0. Entry

  1. 메모리가 할당되면, Eden 영역에서 관리된다.

1. Stop The World(STW)

  • GC가 발생할때마다, GC 스레드를 제외한 모든 스레드가 중단된다.
  • GC가 종료된 후 재개된다.

2. Minor GC - Eden

  1. 도달할 수 없는 객체를 찾아 해제한다.
  2. 남아 있는 객체를 Suvivor 영역으로 이동한다.

3. Minor GC - Survivor

  1. Survivor 영역이 가득차면, 살아있는 객체만 골라 다른 Suvivor 영역으로 모두이동한다.
  2. 특정 객체가 계속해서 살아있거나, 살아있는 객체만으로 가득 찬 경우 오래된 객체를 Old 영역으로 이동한다.
  • 계속 다른 Suvivor 로 이동하는 이유는, 메모리 단편화를 완화하기 위해서이다.
  • 2번과정을 aging이라 부른다.

4. Major GC - old

  1. 살아있는 객체만 남긴다.

추가로 보면 좋은 것들

  • Bump The Pointer
    • 빠른 할당을 위한 기법
  • TLABs
    • Eden 영역을 thread_local 로 처리하여 동시성 문제 해소

평가 기준

  • 시간 대비 처리량(처리 속도)
  • 대기 시간(STW)

GC 선택이 중요한 이유

gc 시간 비율(세로), 프로세서 수(가로)

GC의 종류

GC Selection

  • 총 메모리 100MB 이하 -> Serial GC
  • 싱글 프로세서 && STW가 중요 -> Serial GC
  • MAX_STW_REQUIRE >= 1s && 성능 -> Parallel GC
  • STW가 작아야하고 응답시간이 중요한 경우 -> G1 GC
  • 응답시간이 가장 중요한 경우 -> Z GC
  • 메모리 해제가 필요하지 않고, 첫 메모리로 충분한 경우 -> Epsilon GC

GC Selection

  • Serial GC: XX:+UseSerialGC
  • Parallel GC: XX:+UseParallelGC
  • G1 GC: -XX:+UseG1GC
  • Z GC: -XX:UseZGC

GC Heap Options

  • 메모리 최소: -Xms{size}
  • 메모리 최대: -Xmx{size}
  • Young:Old 크기 비율: -XX:NewRaito
  • Young의 크기: -XX:NewSize
  • Eden:Survivor 크기 비율: -XX:Survivor Raito

Reachable

  • 도달할 수 있음 = Live Object
  • 도달할 수 없음 = Garbage

Reachable

  • Strongly 도달할 수 있음
  • 도달할 수 없음 = Garbage

Reachable

  • Strongly Reachable
  • Softly Reachable
  • Weakly Reachable
  • Phantom Reachable
  • Unreachable

언제?

메모리가 여유로우면 유지하고,부족하면 제거해도 좋을때
=> 있으면 좋지만 없어도 로직상 문제가 되지 않는 객체
ex) 캐시 등

Reference Object

  • Strongly Reachable
  • Softly Reachable
  • Weakly Reachable
  • Phantom Reachable
  • Unreachable

Reference Object

  • A: Softly Reachable
  • B: Weakly Reachable

Reference Object

  • A: Weakly Reachable
  • B: Weakly Reachable

Reference Object

  • A: Weakly Reachable
  • B: Strongly Reachable
var softRef = new SoftReference<T>(new T());
var weakRef = new WeakReference<T>(new T());
// var phantomRef = new PhantomReference<T>(new T(), ...);

T ref;

ref = softRef.get();
ref = weakRef.get();

Thank you!

Reference(1/2)

Reference(2/2)