ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 왜 Minor GC는 S0과 S1을 두고 사용하는가?
    엔진/JVM 2024. 4. 7. 18:53

     

    매주 진행하고 있는 스터디의 이번 주 주제는 'JVM의 GC' 였습니다~

    잘 정리된 블로그 글을 읽다가 생긴 궁금증에 대한 내용을 적어봅니다.


    아래의 글은 다음과 같은 순서로 작성되었습니다.

    1. Heap의 사용

    2. 왜 S0과 S1을 구분하는가

     

     

    1. JVM에서 Heap의 사용

     

    1. Heap의 공간 구분

    JVM은 Heap을 크게 Young과 Old로 공간을 나눠서 구분하고, Young을 다시 Eden과 S0, S1의 형태로 나눠서 메모리를 관리합니다.

     

     

    2. 공간 구분의 기준? 

    Heap을 설계할 때 아래의 2가지를 전제로 두고 만들어졌습니다. (Genertaional Hypothesis)

     

    1. 대부분의 객체는 금방 접근 불가능한 상태가된다. (Weak Generational Hypothesis)

    대부분의 객체는 생성 직후에 사용되고 이후에는 사용되지 않는다. (= 짧은 LifeCycle)
    Young Generation을 두고 자주 GC하는 근거

     

    2. 상대적으로 소수의 객체만 오래 사용된다. (Strong Generational Hypothesis)

    일부 객체는 장시간 사용된다. (= 1번 가정의 확장)
    Old Genernation을 두고 낮은 빈도로 GC하는 근거

     

     

    3. 데이터 저장 방법

    흐름1) S0 / S1이 비어있는 경우

    // GC가 진행될 때마다 age를 증가


    Eden에 객체를 저장
    -> Eden이 가득찬 경우 GC 발생
    -> Mark-Sweep
    -> 사용중인 객체는 S0으로 이동

     

    흐름2) S0에 데이터가 있는 경우

    // S1 에 데이터가 있는 경우에는 S1 -> S0으로 데이터 이동
    // S0과 S1 둘 중 하나는 비어있는 상태를 유지

    Eden에 객체를 저장
    -> Eden이 가득찬 경우 GC 발생
    -> Mark-Sweep
    -> Eden에서 사용 중인 객체 + S0에서 여전히 사용 중인 객체를 S1로 이동
    -> S0은 초기화

     

    흐름3) Old로 데이터가 이동하는 경우

    // age가 특정 임계값을 넘는 경우 young -> old로 이전
    // age에 4bit를 사용하는 경우 15 이후에 이전됨
    // 옵션으로 age를 조정할 수 있음 ( -XX:MaxTenuringThreshold )
    // 사이즈가 큰 객체가 잦은 GC가 발생하는 Young에 존재할 경우 성능 비효율이 발생할 수 있음
    // 사이즈가 큰 객체는 old에서 관리하는 경향 (-XX:PretenureSizeThreshold )

    Eden에 객체를 저장
    -> Eden이 가득찬 경우 GC 발생
    -> Mark-Sweep
    -> Eden에서 사용 중인 객체 + S0에서 여전히 사용 중인 객체 확인
    -> age가 임계치 이상인 경우는 Old로 이동

     

     

     

     

    2. 왜 Minor GC는 S0과 S1을 두고 사용하는가?

    오늘 글을 쓰게 된 이유입니다. 처음에는 왜 새로운 공간에 굳이 다시 할당을 거듭하는가? 에 대한 의문이 생겼습니다.

    '쓰는 객체를 선별했다면 나머지 안쓰는 객체를 제거하면 되는게 아닌가?'라는 접근이었습니다.

     

    우선 Mark-Sweep 에 대해서 생각해볼 수 있습니다. 간단하게 표현한다면

    '쓰이는 객체와 안쓰이는 객체를 구분 -> 안쓰이는 객체를 제거 -> 메모리 공간을 재정리한다' 라는 흐름으로 볼 수 있습니다.

     

    2-1. 만약 1개의 공간만 사용한다면?

    우리가 1개의 공간을 사용하게 된다면 GC 과정에서 아래와 같은 흐름으로 동작할 것입니다.

    1. 쓰이는 객체를 찾는다.
    2. 메모리 공간에서 안쓰이는 객체를 찾아서 제거한다.
    3. 효율적인 메모리 사용을 위해서, 메모리 재정리 
    4. 남은 객체에 대해서 age를 증가시킨다.

     

    2-2. 만약 2개의 공간을 사용하면?

    하지만 2개의 공간을 사용하게 된다면 이렇게 동작합니다.

    1. 쓰이는 객체를 찾는다.
    2. 비어있는 다른 공간에 복사한다.
    3. 복사된 객체의 age를 증가시킨다.
    4. 기존 공간은 비운다.

     

    두 방법에서 동일한 프로세스를 제외하면 아래의 차이점을 확인할 수 있습니다.

    2-1. 메모리 공간에서 안쓰이는 객체를 찾아서 제거 + 메모리 공간 재정리

    2-2. 비어있는 공간에 복사 + 기존 공간 비우기 

     

     

    2-2번의 방법을 사용할 경우 복잡한 compaction(메모리 재정리) 로직에 대해 신경쓰지 않아도 되기 때문에 복잡한 메모리 관리로 부터 자유로워지기 때문에 2번을 채택한 것으로 볼 수 있습니다.

     

    같은 맥락에서 보면 용량이 큰 객체의 경우 Young에서 보관할 경우 성능 저하의 원인이 되기 때문에 Old에서 관리하는 경향을 띄는 것도 납득이됩니다.

     

     

     

     

    감사합니다.

     


    [참고]

    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

Designed by Tistory.