ABOUT ME

-

Total
-
  • Java: Z Garbage Collection (ZGC)
    컴퓨터/JAVA 2023. 1. 15. 15:14
    728x90
    반응형

    ZGC

     

    Main - Main - OpenJDK Wiki

     

    wiki.openjdk.org

     

    오늘은 Z Garbage Collection에 대하여 공부한 대로 글을 써본다.

    Java 11 부터 실험적으로 추가되어 Java 15에서는 어느새 Production Ready (PR) 상태까지 온 미래 GC 후보라고 볼 수 있다.

    (Z의 의미는 아무 뜻도 없다고 한다.)

     

    목차는 다음과 같다.

    • Introduction (소개)
    • Basics of ZGC (ZGC 기본 지식)
    • ZGC phases (ZGC 단계)
    • Concurrent reference processing in ZGC (concurrent 레퍼런스 처리 in ZGC)
    • Advanced Topics (심화 주제)
    • Conclusion (결론)

     

    ◆ Introduction

    Z Garbage Collector (ZGC)는 대용량 heap을 위해 설계된 저지연(low-latency) 가비지 컬렉터이다.

    GC 포즈 시간(< 10ms)을 최소화하고 높은 애플리케이션 처리량을 유지하는 것을 목표로 하는 동시 및 병렬 GC 알고리즘입니다.

    ZGC의 고유한 기능 중 하나는 실행 중인 프로그램의 중단을 최소화하면서 응용 프로그램 스레드와 동시에 대부분의 작업을 수행할 수 있다는 것이다.

    또한 sliding mark window을 사용하여 heap의 live 객체를 표시하는 데 소요되는 시간을 줄인다.

    다른 GC 알고리즘과의 비교 측면에서 ZGC는 GC 포즈 시간을 최소화하고 큰 힙 크기를 지원한다는 점에서 G1(Garbage First)과 유사하다.

    그러나 ZGC는 동시 및 병렬 설계로 인해 더 낮은 포즈 시간과 더 높은 처리량을 달성할 수 있다.

    반면, CMS(Concurrent Mark and Sweep)는 단일 스레드 sweep 단계로 인해 긴 포즈 시간을 겪는 일반적인 concurrent GC 알고리즘이다.

    이제 ZGC가 어떻게 작동하는지 간단히 살펴보자.

     

    ZGC의 전반적인 작동 방식:

    ZGC는 GC 프로세스를 크게 4단계로 볼 수 있다:

    1. Marking (M): 이 단계에서 ZGC는 heap의 모든 live 개체를 식별한다. GC root에서 시작하여 heap의 다른 개체에 대한 모든 참조를 추적함으로써 이 작업을 수행하고 이 과정을 마킹이라고 한다.
    2. Relocation (R): 이 단계에서 ZGC는 대피 후보(EC)라고 불리는 힙의 페이지 집합을 식별한다. live 개체의 밀도가 낮으며 힙의 다른 부분으로 안전하게 재배치할 수 있는 페이지이다. 그런 다음 ZGC는 모든 활성 개체를 EC 페이지에서 비 EC 페이지로 동시에 이동한다.
    3. Remapping (RE): 이 단계에서 ZGC는 재배치된 개체에 대한 모든 참조를 새 위치를 가리키도록 업데이트한다. (new pointer) 이 작업은 Relocation 단계에서 생성된 Forwarding table을 업데이트하여 수행된다.
    4. Reclaim (RC): 마지막 단계에서 ZGC는 완전히 제거된 모든 EC 페이지를 회수한다.

    ZGC의 주요 특징 중 하나는 슬라이딩 마크 창을 사용하는 것이다.

    이것은 ZGC가 여러 GC 사이클에 걸쳐 마킹 작업을 분산시킬 수 있도록 하는 시간 기반 마킹 접근법이다.

    M 단계에서 ZGC는 힙을 Marked0 (M0)과 Marked1(M1)의 두 영역으로 나눈다.

    M 단계가 시작되면 ZGC는 M0의 모든 개체를 표시한 다음 다음 GC 사이클에서 M1의 모든 개체를 표시한다.

    이 과정은 후속 GC 사이클에서 반복되며, M0과 M1 영역은 힙을 가로질러 슬라이딩된다.

     

    ◆ Basics of ZGC

     

    F, R, M1, M0 bits

    F, R, M1 및 M0 비트에 대한 설명:

    ZGC에서 힙의 각 객체는 다음과 같은 4가지 비트의 조합으로 표시:

    Finalizable(F), Remapped(R), Marked1(M1), Marked0(M0)

    F 비트는 객체가 수집되기 전에 실행되어야 하는 finalize 메소드가 있음을 나타내기 위해 사용된다.

    R 비트는 개체가 힙의 새 위치로 재배치되었음을 나타내는 데 사용된다.

    M1 비트는 GC 사이클의 마킹 단계에서 객체가 활성 상태임을 나타내기 위해 사용된다.

    M0 비트는 이전 GC 사이클에서 객체가 표시되었음을 나타내기 위해 사용된다.

     

    ZGC가 강력한 삼색 불변량을 유지하는 방법

    (black - grey - white)

    ZGC에서 strong tricolor invariant은 가비지 수집 과정 전반에 걸쳐 유지되는 속성으로, black 포인터만 사용하여 힙의 모든 살아있는 객체가 객체 그래프의 루트에서 도달할 수 있도록 보장한다.

    이것은 Load Barrier을 사용함으로써 달성되는데, 이는  black objects에서 white objects까지의 모든 포인터가 mark stack에 푸시되도록 함으로써 삼색 불변을 유지하는 메커니즘이다.

    mark stack은 가비지 수집기에서 힙에 있는 모든 활성 개체를 표시하는 데 사용되며 Mutator 스레드와 동시에 처리된다.

    이 프로세스는 블랙 포인터만을 사용하여 힙의 모든 살아있는 객체가 루트에서 도달할 수 있도록 보장하므로 강력한 삼색 불변성이 유지된다.

     

    Load Barrier 및 thread-safety을 보장하는 방법

    스레드 안전성을 보장하기 위해 ZGC는 로드 배리어를 사용한다.

    부하 장벽의 목적은 변환기 또는 응용 프로그램 스레드가 GC에 의해 이동되거나 수정되는 중인 힙의 개체에 액세스 하지 않도록 하는 것이다.

    로드 장벽은 힙에 있는 개체에 대한 참조 로드를 가로채고 로드가 진행되도록 허용하기 전에 검사를 수행하는 방식으로 작동한다.

    만약 객체가 mutator에 의해 접근되어서는 안 되는 상태에 있다면, load는 GC가 필요한 작업을 안전하게 완료할 수 있는 slow path로 리디렉션 된다.

    로드 배리어가 확인하는 두 가지 주요 상태는 이동 중인 개체(F, R 비트 세트)와 마킹 중인 개체(M1 비트 세트) 이다.

    액세스 중인 개체가 이러한 상태 중 하나일 경우 로드가 slow path로 리디렉션 된다.

    로드 배리어는 또한 M0 비트를 사용하여 현재 GC 사이클 동안 표시된 개체를 추적한다.

    이를 통해 GC는 어떤 객체가 활성화되어 있고 어떤 객체가 안전하게 회수될 수 있는지를 효율적으로 결정할 수 있다.

    전체적으로 load barrier은 mutator와 GC가 힙의 객체에 동시에 안전하게 접근하고 수정할 수 있도록 한다.

     

    ◆ ZGC phases

    @ALBERT MINGKUN YANG, Oracle

     

    STW1 (Stop The World) 단: (Initial Mark로도 불림)

    1. 모든 스레드가 현재의 좋은 색상에 동의합니다. 이는 M0 또는 M1 비트가 교대 선택으로 설정됨을 의미합니다. 
    2. 새로운 페이지가 생성되어 현재 사용 중인 할당 페이지를 대체하여 mutator가 사용할 수 있도록 합니다. 이후 할당은 이러한 새로운 페이지에 배치됩니다.
    3. 시스템 클래스 및 객체, 스택 프레임의 참조 등과 같은 루트에 대해 mark barrier가 적용됩니다.
    4. mark barrier는 유효하지 않은 포인터를 감지하고 필요한 경우 주소를 업데이트하고 좋은 색상으로 염색하여 자체 치유합니다.
    5. mark barrier는 또한 루트를 mark stack에 푸시합니다. 

    전반적으로 STW1 단계는 세 가지 주요 작업을 수행합니다.

    현재 GC 주기에 대한 공유의 좋은 색상을 설정하고, mutator가 사용할 수 있는 새로운 페이지를 할당하며, 루트에 mark barrier를 적용하여 좋은 색상을 가지고 mark stack에 추가되도록 합니다.

     

    Mark and Relocate (M/R) 단계:

    1. STW1 단계 이후 marking/remapping 단계가 시작됩니다. 
    2. GC 스레드는 mark stack을 소비하여 popped 객체를 표시하고 관련 페이지의 liveness 정보를 업데이트합니다. liveness 정보는 페이지의 live 바이트 수이며 heap의 defragmenting의 일부로 객체를 evacuate 하는 페이지를 선택하는 데 사용됩니다.
    3. mark_obj 함수가 객체를 표시하기 위해 호출됩니다. 이 함수는 atomic operation (CAS)을 사용하여 비트맵에서 비트를 설정하여 스레드 안전하게 만듭니다. 객체가 이전에 표시되지 않았고 현재 스레드가 성공적으로 표시하면 mark barrier (MB)가 객체의 참조 유형 필드에 적용됩니다.
    4. MB는 load barrier에서 barrier skeleton을 사용하고 나쁜 색상의 포인터를 좋은 색상과 일치하도록 업데이트합니다. 포인터가 이전 GC에서 evacuation candidate set (EC)을 가리키면 좋은 색상의 새 위치로 remap 됩니다. 그렇지 않으면 meta 비트가 단순히 좋은 색상과 일치하도록 변경됩니다. 좋은 색상의 포인터는 그다음 mark stack에 푸시되어 슬롯에 다시 작성됩니다.
    5. mutator가 heap에서 포인터를 로드할 때 적용되는 load barrier는 mark barrier와 매우 유사합니다. 이는 black mutator를 사용하는 increment-update 기술을 사용하여 강한 tricolor 불변성을 유지합니다.
    6. MB는 load barrier skeleton과 다르게 void 반환 유형을 가지고 있으며 좋은 색상의 포인터조차도 slow path에 강제로 진입하도록 합니다. 이는 mutator와 GC 스레드가 자체 thread-local marking stack을 가지고 있기 때문이며 GC mark stack에 가능한 많은 객체가 있기를 원합니다. 이는 mutator의 thread-local mark stack의 항목이 marking이 완료되기 전에 GC 스레드에 의해 처리되기 때문에 안전합니다.
    7. 그러나 다른 스레드에 속하는 항목을 처리하면 일정한 동기화 비용이 발생합니다. F1, M0, M1 및 R 비트는 marking 단계 중 객체의 liveness 및 reachability를 추적하는 데 사용됩니다. F1 비트는 비어 있지 않은 finalize() 메서드가 있는 객체에 설정되며 어떤 객체가 최종화 되어야 하는지 추적하는 데 사용됩니다.
    8.  M0 및 M1 비트는 객체의 marking 상태를 추적하고 tricolor 불변성을 유지하는 데 사용됩니다. R 비트는 defragmentation 프로세스 중 어떤 객체가 이동되었는지 추적하는 데 사용됩니다.

    전반적으로 marking/remapping 단계에서 GC 스레드는 mark stack을 소비하여 그 위의 객체를 표시하고 관련 페이지의 liveness 정보를 업데이트합니다. mark barrier는 객체의 참조 유형 필드에 적용되며 나쁜 색상의 포인터를 업데이트하고 mark stack에 푸시합니다. load barrier도 mutator가 heap에서 포인터를 로드할 때 적용되며 mark barrier와 유사한 기능을 합니다. F1, M0, M1 및 R 비트는 이 단계에서 객체와 포인터의 상태를 추적하는 데 사용됩니다.

     

    STW2 단계:

    1. marking 단계의 종료 조건이 검사 됩니다. 모든 mark stack이 비어 있으면 marking 단계가 종료되고 STW2 단계가 시작됩니다.
    2. mark stack 중 하나라도 비어 있지 않으면 marking 단계가 계속되고 mutator가 다시 시작할 수 있습니다. GC 스레드는 mark stack을 소비합니다.
    3. 각 mutator와의 thread-local handshaking이 수행되어 STW pause를 시도하기 전에 표시해야 할 객체의 존재 여부를 확인합니다. 더 이상 표시할 객체가 없으면 STW2 단계가 시작됩니다.

    전반적으로 STW2 단계는 모든 mark stack이 비어 있는지 확인하고, 그렇다면 marking이 완료됩니다. 그렇지 않으면 STW2는 다른 STW pause를 시도하기 전에 각 mutator와의 thread-local handshaking을 계속 수행하여 표시해야 할 객체의 존재 여부를 확인합니다. 이는 STW2가 조기에 진입할 확률을 줄이고 mutator 처리량에 미치는 영향을 줄입니다.

     

    RP (Reference Processing):

    • RP 단계는 STW2 pause로 시작하여 모든 mutator가 중지됩니다.
    • reference queue에 등록된 모든 Soft, Weak 및 Phantom 참조가 처리됩니다.
    •  reference handler 스레드(JVM에 의해 생성된 저 우선순위 스레드)는 reference queue를 스캔하여 enqueue 된 참조를 제거합니다. 이러한 제거된 참조의 referent(참조가 가리키는 객체)는 R 비트로 표시됩니다.
    • RP 단계가 종료되고 concurrent reference processing 단계가 시작됩니다. reference handler 스레드는 mutator와 동시에 reference queue의 참조를 계속 처리합니다.
    • 발견된 모든 referent는 R 비트로 표시됩니다. reference handler 스레드가 reference queue의 모든 참조를 처리 완료하면 concurrent reference processing 단계가 종료됩니다.

     

    Type of Reference Description
    Soft A soft reference is cleared at the discretion of the garbage collector in response to memory demand.
    Weak A weak reference is cleared immediately after the garbage collector determines that the object is weakly reachable.
    Phantom A phantom reference is the weakest type of reference and is only used for scheduling pre-mortem cleanup actions. The garbage collector clears a phantom reference immediately after the object becomes phantom reachable.
    Final A final reference is used internally by the JVM for objects that have a non-empty finalize() method. It is not visible to developers.

     

     

    STW3 단계:

    1. R 비트가 설정된 포인터의 좋은 색상이 변경됩니다. 이는 이 색상의 포인터가 evacuation candidate (EC) 페이지의 객체를 가리키지 않음을 보장한다는 의미입니다.
    2. 모든 루트가 방문되고 EC 페이지로 가리키는 루트는 non-EC 페이지로 이동됩니다. EC 페이지로 가리키지 않는 루트는 단순히 좋은 색상으로 염색됩니다. 이렇게 하면 루트가 EC 페이지로 가리키지 않습니다.
    3. load barrier의 slow path가 업데이트됩니다. 이는 mutator가 EC 내의 객체에 액세스 하기 전에 EC 외부의 새 페이지로 이동하게 됨을 의미합니다. STW3에서 루트 처리와 함께 mutator가 EC로 가리키는 포인터를 볼 수 없는 불변성이 설정됩니다.

    전반적으로 M/R 단계 중에 live로 확인된 모든 객체가 heap의 새 위치로 이동됩니다.

     

    RP (reference processing) 단계:

    1. 처리해야 할 가능성이 있는 모든 non-strong 참조를 식별(발견)하고 discovered list (DL)에 배치합니다. resurrection-blocked window에 들어가서 resurrection을 비활성화합니다.
    2. 이 창에서 참조의 get() 메서드 호출은 referent의 marking 상태에 따라 referent 또는 null을 반환합니다. M/R 단계에서 얻은 완전한 reachability 정보를 사용하여 처리되어서는 안 되는 참조를 필터링하여 discovered list를 정리합니다.
    3. discovered list의 모든 남은 참조를 non-active로 표시하여 처리합니다. resurrection-blocked window에서 나와 resurrection을 다시 활성화합니다.
    4. discovered list를 발행하여 참조가 등록된 큐(있는 경우)에 배치될 수 있도록 합니다. 

    전반적으로 garbage collector는 non-strong 참조를 처리하며, 완전한 marking 정보를 사용하여 처리해야 할 참조를 결정하고 등록된 큐에 추가합니다.

     

    * non-strong 참조란 객체가 garbage collected 되지 않도록 방지하지 않는 객체에 대한 참조입니다.

    * the resurrection-blocked window is a period of time during which objects that are no longer reachable cannot be resurrected.

     

    대피 후보(EC) 선정:

    • 이전 GC 주기의 대피 후보 세트는 초기화되며, 연관된 포워딩 테이블은 제거됩니다.
    • 적어도 하나의 살아있는 객체를 가진 이동 가능한 페이지들이 잠정적으로 EC 세트에 추가되며, 살아있는 객체가 없는 페이지들은 회수됩니다.
    • EC 세트 안의 페이지들은 페이지 위의 살아있는 바이트 수에 따라 정렬됩니다.
    • 단편화 한도 임계값에 기반하여 분기점(cut-point)이 선택되며, 이 점 이후의 페이지들은 EC 세트에서 제거됩니다. 이렇게 하면 EC 세트는 작은 크기와 중간 크기 클래스의 페이지들만을 가지게 됩니다.
    • 큰 크기의 페이지들, 즉 단 하나의 살아있는 객체나 죽은 객체만을 포함하는 페이지들은 재배치에 참여하지 않으며, 큰 객체들은 절대 재배치되지 않습니다.



    전반적으로, EC 단계에서는 이전 GC 주기의 대피 후보 세트가 초기화되며,

    적어도 하나의 살아있는 객체를 가진 이동 가능한 페이지들이 잠정적으로 EC 세트에 추가되고, 살아있는 객체가 없는 페이지들은 회수됩니다.

    EC 세트 안의 페이지들은 살아있는 바이트 수에 따라 정렬되고, 단편화 한도 임계값에 기반하여 분기점이 선택됩니다.

    결과적으로 EC 세트는 작은 크기와 중간 크기 클래스의 페이지들만을 포함하게 되며, 큰 크기의 페이지들과 객체들은 재배치에 참여하지 않습니다.

     

    STW3 단계:

    재배치로의 전환

    • STW3에서, 좋은 색깔(good color)은 R 비트 설정(0100)을 가진 색으로 변경됩니다.
    • 모든 루트는 방문되며, 그들이 EC(대피 후보) 페이지를 가리키는지 확인합니다.
    • 루트가 EC 페이지를 가리키면, 해당 객체는 EC가 아닌 페이지로 재배치됩니다.
    • EC 페이지를 가리키지 않는 루트는 좋은 색으로 칠해집니다.
    • 로드 바리어 로직의 슬로우 경로는 EC 페이지를 가리키는 포인터를 체크하도록 업데이트됩니다
    • 변경자(mutator)가 EC 내의 객체에 접근하려고 시도하면, 그것은 접근 전에 EC 외부의 새 페이지로 재배치됩니다.
      이는 변경자가 EC 페이지로의 포인터를 보지 못하게 하는 불변성을 설정합니다.

     

    재배치(RE) 단계:

    • GC 스레드는 EC 페이지를 페이지 단위로 객체들을 재배치하는 작업을 시작합니다.
    • 객체들은 새로운 위치로 이동되며, 그들의 이전 주소는 포워딩 테이블에서 새 주소로 매핑됩니다.
    • 재배치와 동시에 객체에 접근하는 변경자는 로드 바리어의 슬로우 경로를 거치며, GC 스레드를 재배치 작업에 도움이 될 수 있습니다.
    • 만약 변경자가 포워딩 테이블을 업데이트하는데 실패하면, 대신 테이블에서 새 주소를 읽어올 것입니다.
    • EC 페이지의 모든 객체가 재배치되면, RE 단계는 끝나며 EC 페이지들은 회수될 수 있습니다.
    • 만약 객체가 메모리 할당 실패로 인해 재배치할 수 없으면, 이전 주소는 새 주소로 사용되며 EC 페이지는 같은 페이지에 있는 아직 대피되지 않은 객체들이 대피되지 않도록 표시됩니다.
    • RE 단계의 끝에서 EC 페이지는 회수되지 않을 것입니다.

     

    ZGC에서 STW(Stop-The-World) 일시 중단이 어떻게 사용되고 최소화되는지


    Stop-The-World(STW) 일시 중단은 가비지 수집 주기의 특정 부분에서 스레드 안전성을 보장하기 위해 사용됩니다. 이러한 일시 중단은 가비지 수집기가 변경자(mutator) 스레드의 간섭 없이 특정 작업을 수행할 수 있게 하므로, 힙이 일관된 상태를 유지하게 돕습니다.

    STW 일시 중단의 영향을 최소화하기 위해, ZGC는 여러 기법을 사용합니다. 그중 한 가지 기법은 하나의 긴 일시 중단이 아닌 여러 STW 단계를 사용하는 것입니다. STW 일시 중단을 작은 단계로 나눔으로써, 일시 중단의 전체 지속 시간을 줄일 수 있으며, 이로 인해 변경자 스레드에 대한 불응도의 기간이 짧아집니다.

    STW 일시 중단을 최소화하는 또 다른 기법은 동시 참조 처리의 사용입니다. 이를 통해 가비지 수집기가 STW 일시 중단을 기다리는 대신 변경자 스레드와 동시에 참조를 처리할 수 있습니다. 참조 처리는 종종 시간 소모적인 작업이므로, 이는 STW 일시 중단의 길이를 크게 줄일 수 있습니다.

    전반적으로, 여러 STW 단계의 사용과 동시 참조 처리를 통해 ZGC 알고리즘에서 STW 일시 중단의 영향을 최소화하며, 이로 인해 변경자 스레드에 대해 더 일관성 있는 반응성 있는 동작을 가능하게 합니다.

     

    ◆ ZGC에서의 동시 참조 처리


    동시 참조 처리의 도전 과제


    ZGC에서 동시 참조 처리의 한 가지 도전 과제는 부활(resurrection)과 클리어링(clearing) 사이의 경쟁입니다. 이는 변경자가 참조에서 get() 메서드를 호출하고 GC 스레드가 참조를 클리어하려고 시도하는 동시에 발생할 수 있습니다. 변경자의 부활이 참조 대상의 도달 가능성을 변경하면, 이는 GC 스레드의 클리어링 결정을 무효화할 수 있습니다.

    또 다른 도전 과제는 여러 참조 간의 일관된 뷰를 보장하는 것입니다. 여러 참조가 공통 참조 대상을 공유할 때, 참조 대상이 약하게 도달 가능해지면, 모든 참조는 동시에 클리어되어야 합니다. 이가 발생하지 않고 비 클리어 참조를 통해 부활이 발생하면, 클리어링 결정이 무효화될 수 있습니다. 이 도전 과제를 해결하기 위해, GC 스레드와 변경자 모두가 참조 대상의 도달 가능성에 대해 일관된 뷰를 가지는 것이 필요합니다.



    ZGC가 이 도전 과제를 어떻게 해결하고 참조 처리를 동시에 수행하는지


    부활과 클리어링 사이의 경쟁을 해결하기 위해, ZGC는 락-프리 참조 처리라는 기법을 사용합니다. 이는 원자적 연산을 사용하여 참조의 상태를 업데이트하고, 여러 스레드가 동시에 참조에 접근하고 수정하더라도 적절한 작업이 수행되도록 보장합니다.

    여러 참조 간의 일관된 뷰를 보장하기 위해, ZGC는 참조 발견이라는 기법을 사용합니다. 이는 공통 참조 대상을 공유하는 모든 참조의 목록을 유지하고 그들이 모두 함께 처리되도록 보장하는 것을 포함합니다. 이로 인해 참조 대상의 도달 가능성이 일관되게 평가되고 모든 참조가 동시에 클리어되거나 부활되도록 합니다.

    전반적으로, ZGC는 이러한 기법을 사용하여 필요한 일관성과 정확성을 유지하면서 참조 처리를 동시에 수행할 수 있습니다

     

    ◆ 고급 주제

    ZGC의 메모리 레이아웃과 큰 객체 처리 방법


    ZGC에서의 메모리 레이아웃은 소형 및 중형 페이지와 큰 페이지로 구성됩니다. 소형과 중형 페이지는 특정 크기 임계값 이하의 객체를 저장하는 데 사용되며, 큰 페이지는 이 임계값보다 큰 객체를 저장하는 데 사용됩니다.

    ZGC에서 큰 객체를 처리하는 한 가지 방법은 가비지 수집 중에 이들을 재배치하지 않는 것입니다. 큰 객체는 재배치되지 않기 때문에, 힙의 단편화에 기여하지 않으며, 포워딩 포인터의 사용이 필요하지 않습니다. 대신 큰 객체는 마크/재매핑 단계에서 라이브 또는 데드로 표시되며, 데드인 큰 객체는 참조 처리 단계에서 직접 회수됩니다.

    또 다른 방법은 큰 객체가 소형 또는 중형 객체와 같은 페이지에 저장되지 않도록 하는 것입니다. 이는 큰 객체에 의해 소형 및 중형 객체가 밀려나는 것을 방지하고 메모리 사용을 개선하는 데 도움이 됩니다.

    전반적으로, ZGC의 큰 객체 처리 방식은 가비지 수집의 오버헤드를 줄이고 시스템의 성능을 향상하는 데 도움이 됩니다.


    ZGC의 Java의 비힙 메모리(예: 직접 버퍼) 지원

    ZGC는 로드 배리어를 사용해 직접 버퍼 같은 Java의 비힙 메모리를 처리할 수 있습니다. 로드 배리어는 힙 내의 객체에 대한 액세스를 가로채는 메커니즘으로, JIT 컴파일러를 사용해 구현됩니다. 변경자 스레드가 비힙 메모리 내의 객체에 접근하려고 시도할 때, 로드 배리어가 트리거 되고 액세스는 객체의 도달 가능성을 판단할 수 있는 느린 경로로 리디렉션 됩니다. 객체가 도달 가능하면, 액세스는 계속 진행됩니다. 객체가 도달 불가능하면, 그것은 가비지로 간주되며 수집 대상이 됩니다.

    비힙 메모리를 처리하기 위해 로드 배리어를 사용하는 주요 이점 중 하나는, ZGC가 변경자 스레드의 실행과 동시에 가비지 수집을 수행할 수 있다는 것입니다. 이는 변경자 스레드가 가비지 수집이 진행되는 동안 계속 실행될 수 있음을 의미하며, 이로 인해 일시 중단 시간이 줄어들고 전반적인 성능이 향상됩니다.

    로드 배리어를 사용해 비힙 메모리를 처리하는 것 외에도, ZGC는 이러한 유형의 객체에 대한 가비지 수집의 효율성을 향상하기 위해 설계된 여러 기능을 지원합니다. 예를 들어, ZGC는 직접 버퍼와 같은 큰 객체를 가비지 수집의 오버헤드를 줄이는 특별한 방식으로 처리할 수 있습니다. 큰 객체는 가비지 수집 중에 재배치 대상이 아니므로 메모리 내에서 이동할 필요가 없고 효율적으로 수집될 수 있습니다. 이는 가비지 수집의 전반적인 비용을 줄이고 시스템의 성능을 향상하는 데 도움이 됩니다.


    ZGC가 클래스 언로딩을 처리하는 방법

    클래스 언로딩은 클래스 정의를 Java 가상 머신(JVM)에서 제거하여 어떤 실행 중인 Java 애플리케이션에서도 클래스에 액세스 할 수 없게 하는 과정입니다. 이는 일반적으로 애플리케이션에서 더 이상 사용되지 않는 메모리를 회수하기 위해 수행됩니다. Java 프로그래밍 언어에서 클래스는 객체를 생성하기 위한 템플릿이며, 클래스 정의에는 객체가 사용할 필드와 메서드가 포함됩니다. 클래스가 더 이상 필요하지 않을 경우, JVM은 클래스를 언로드 하여 클래스 정의에 의해 차지되었던 메모리를 해제할 수 있습니다.

    ZGC 가비지 수집기에서 클래스 언로딩은 나머지 GC 사이클과 동시에 처리됩니다. 이는 일반적으로 STW(Stop-The-World) 단계에서 별도로 클래스 언로딩을 수행하는 다른 GC와 대조적입니다.

    ZGC에서 클래스 언로딩은 M/R 단계에서 시작되며, 이 단계에서 라이브 객체가 마킹되고 각 페이지의 생존 여부가 결정됩니다. 페이지에 라이브 객체가 없는 것으로 판단되면, 그 페이지는 클래스 언로딩 대상 페이지의 후보 목록에 추가됩니다. 이 목록은 모든 라이브 객체가 재배치된 후 RE 단계에서 처리됩니다.

    후보 목록을 처리하는 동안, GC는 페이지에 있는 클래스 중 어느 것이 여전히 Java 가상 머신(JVM)에 로드되어 있는지 확인합니다. 클래스가 로드되어 있지 않다면, 클래스는 언로드 되고 페이지는 회수하기 위해 무료 리스트에 추가됩니다. 클래스가 여전히 로드되어 있다면, 페이지는 후보 목록에서 제거되고 그 페이지의 객체는 현재 GC 사이클에 대해 라이브로 취급됩니다.

    동시 클래스 언로딩에서 가능한 문제 중 하나는 변경자와 GC 스레드 사이의 경쟁입니다. 변경자가 GC 스레드에 의해 언로드 중인 페이지의 객체에 접근하려고 시도하면, 변경자는 객체의 클래스를 로드하려고 시도하고 이를 JVM에 유지시키려고 할 수 있습니다. 이를 방지하기 위해, ZGC는 변경자에 의한 클래스 로딩 시도를 가로채고 클래스 언로딩 프로세스가 완료될 때까지 지연시키는 로드 배리어를 사용합니다.

    전반적으로, ZGC의 동시 클래스 언로딩은 기 traditional 한 클래스 언로딩 기법과 관련된 STW 일시 중단을 줄이는 데 도움이 되어 GC의 전반적인 성능과 확장성을 향상합니다.

     

    ◆ 결론


    ZGC는 Java 11에서 실험적으로 도입된 동시 가비지 수집기입니다. 이는 큰 힙 크기와 많은 객체 할당을 요구하는 애플리케이션에 대해 낮은 지연 시간과 높은 처리량을 제공하려는 목표를 가지고 있습니다. ZGC의 핵심 기능 중 하나는 이동하지 않는 큰 객체를 효율적으로 처리할 수 있는 능력입니다. 이는 가비지 수집의 오버헤드를 줄이고 수집기가 증가하는 힙 크기에 대해 더 나은 확장성을 가지도록 돕습니다.

    다른 GC 알고리즘과 비교하여 ZGC는 여러 가지 이점과 단점이 있습니다. 주요 이점 중 하나는 낮은 일시 중단 시간으로, 이는 높은 처리량 요구사항을 가진 애플리케이션에 적합합니다. 또한 G1 GC와 같은 다른 동시 수집기에 비해 상대적으로 낮은 메모리 오버헤드를 가지고 있습니다. 그러나 ZGC는 작은 힙 크기를 가진 애플리케이션에는 적합하지 않습니다. 이는 스레드 로컬 마킹 스택의 사용으로 인해 이러한 경우에 더 높은 오버헤드를 가지기 때문입니다.

    전반적으로, ZGC는 큰 힙 크기와 높은 할당률에 대해 좋은 성능을 제공하는 유망한 GC 알고리즘입니다. 그러나 아직 실험적인 기능이므로 현재 시점에서 모든 애플리케이션에 적합하지 않을 수 있습니다.

     

    728x90

    댓글