가비지 컬렉터 (3) - G1 GC

블러거·2026년 1월 15일

JVM

목록 보기
9/26

G1 GC

대용량 메모리를 갖춘 멀티프로세서 시스템을 대상으로 합니다. G1은 최소한의 설정으로 높은 처리량을 달성하면서 가비지 컬렉션 일시 중지 시간 목표를 높은 확률로 충족하도록 설계되었습니다. G1은 다음과 같은 특징을 가진 현재의 대상 애플리케이션 및 환경을 사용하여 지연 시간과 처리량 간의 최적의 균형을 제공하는 것을 목표로 합니다.
https://docs.oracle.com/en/java/javase/11/gctuning/garbage-first-g1-garbage-collector1.html

  • 목표한 짧은 정지 시간을 유지하며 최대한의 처리량을 끌어내는 두가지 목표의 균형
    • 목표 정지 시간을 지정할 수 있고, G1 GC 는 그 시간을 지키려 노력
    • 옵션 조정을 통해 더 높은 처리량 or 짧은 정지 시간 택1의 조정도 가능
  • 리전간 복사는 mark-copy, 정리떄에 한 리전으로 모인다는 개념은 mark-compact
  • JDK 9부터 기본 컬렉터
  • CMS 를 완전히 대체
  • Young/Gen 영역용 GC 로 구분되지 않는 GC 이다. G1 GC 는 Young/Old 모두를 처리한다.
    • -XX:+UseG1GC 로 명시적으로 활성화 할 수 있다.

주요 개념

  1. 일시 정지 시간 조절 : Young GC 기록을 통해 Young Gen 의 크기를 수정함. GC 에 걸리는 시간이 조절됨.
  2. 일시 정지 시간 조절 : Marking 은 Concurrent(SATB)
  3. 일시 정지 시간 조절 : CMS 에 비해 Compact 가 있으므로 메모리 단편화 X. Full GC 의 빈도가 낮음.
  4. 높은 처리량 : 살아남은 객체를 Copy, Compact 하는 과정(오래 걸리는 작업)은 STW 상태에서 병렬로 실행됨
  5. 둘 다
  • Concurrent Marking 단계에서 공간이 큰 Old 영역에 대한 CSet 의 선정을 위한 작업을 동시에 실행하여 일시 정지 시간을 줄임
  • Mixed GC 시 CSet 만 Scan 하여 높은 처리량(짧은 GC 시간)
  • Reclamation Efficiency 기반 GC 로 짧은 시간에 많은 양의 메모리를 정리

CMS 와 다르게 회수 단계에 Mark-Compact 를 사용한다. 객체를 이동시키는 과정에서는 유저 스레드와 동시에 실행하지 않으므로 이론상 CMS 보다는 정지 시간이 길 수 있다.
하지만 최대한 짧은 시간이 목표인 CMS 와 최대의 처리량과 목표한 정지 시간이라는 G1 의 정지 시간 차이는 납득할 만 하며, 실제로 거의 나지 않는다.

Region

  • G1 에서 힙을 나누는 단위
    • 기존 Serial 같은 연속된 공간의 세대 구분 레이아웃과 다름
  • 크기는 -XX:G1HeapRegionSize 로 설정
    • 설정하지 않는다면 initial 과 max 힙 크기에 따라 달라짐. 힙 내에 2048 개 정도의 region 이 만들어질 수 있는 크기가 셋팅 (크기는 항상 2의 제곱수)
  • Region 단위로 메모리를 관리함으로써, 높은 처리량 달성이 가능
    - Region 단위의 메모리 회수 비용 예측(후술)
  • 모든 Region은 Eden, Survivor, Old 가 될 수 있으며 동적으로 바뀜

Garbage Collection Cycle

Young GC 와 Concurrent marking 을 진행하는Young-Only Phase 와 Mixed GC 진행하는 Space Reclamation Phase 의 반복

  • 그림에서 모든 원은 STW
  • 파란색 작은 원 - Young GC
  • 빨간색 작은 원 - Mixed GC

Concurrent Marking


https://tschatzl.github.io/2022/08/04/concurrent-marking.html

GC Root 를 마킹하는 단계

SATB 를 만들고, Marking 을 시작하기 위해 Root 를 마킹하는 단계이다.
Concurrent Marking 의 시작이며, IHOP 임계점을 넘으면 시작된다.

  1. Concurrent Marking Start
    • STW 단계지만, Young GC 의 STW 와 같이 실행하여 STW 최소화.
      • Young GC 에 piggyback 한다. 이 때 승격된 Region 들을 기록한다. 이는 다음 Concurrent Root Region Scan 에서 쓰인다.
    • GC Root 로 부터 직접적인 참조 객체 마킹
    • SATB 생성(TAMS 포인터 셋팅)
  2. Concurrent Root Region Scan
    • Start 단계에서 Marking 한 Region 의 객체들을 root 로 취급하여 마킹한다.
    • SATB 스냅샷에 의해 고정되어 있으므로 Region 만 STW 인 첫번째 단계에서 정해주면 이 단계는 Concurrent 하게 처리할 수 있다.

동시 마킹은 Mixed GC 에서 Old Generation 에서 살아있는 객체를 Marking 하는 것이다.
Old 영역에서 살아남아야 할 객체는 다음 두가지로, 둘 다 Root 로서 마킹하고 객체 그래프를 순회해야한다.

  1. 스택 등 에서 참조중인 객체
  2. Young Gen 에서 참조중인 객체

1번은 별 문제가 없는데, 2번은 문제가 있다. Young GC 에서 세대간 참조인 Young Gen <- Old Gen 참조는 RSet 을 통해 쉽게 해결이 가능하지만, Old GC 에서 세대간 참조인 이 경우는 RSet 으로 해결이 불가능하다. 따라서 Young Gen 을 모두 스캔하여 Old Gen 으로의 참조가 있는지 체크해야 한다. 이를 위해 Concurrent Marking Start 는 Young GC 에 얹혀 실행되며 Young GC 의 스캔에 껴서 Old Gen 으로의 참조가 있는 Young Region 을 골라둔다.

Young -> Old 로의 세대간 참조가 RSet 만으로 해결되지 않는다는건.. 이 글에서 참고했는데, 근거는 명확하진 않다.
내 생각으로는 oracle 문서 에서 Young Gen 의 RSet 은 항상 유지하지만, Old Gen 의 RSet 은 lazily 하게 완성되기 때문에 완벽하지 않기 때문이지 않나 싶다. Old Gen 의 RSet 을 lazily 하게 세팅되는 이유는 Old Gen 은 리전이 엄청 큰데 Rset 을 유지하면 메모리가 많이 들어서 그렇다. (이슈)

동시 마킹

  1. Concurrent Marking
    • 이전 단계를 통해 Root 객체들은 모두 마킹되어 있음.
    • 비트맵 보고 마킹되어 있으면 거기부터 탐색 시작하여 살아 있는 객체를 마킹.
    • 이 단계 중 TAMS 이하의 객체에 대해 변경 작업이 있으면, SATB Pre-Write-Barrier 로 원래 참조값을 SATB Buffer 에 ADD. 이는 SATB 로 첫 스냅샷 기준으로 Marking 을 진행하기 위해 버퍼에 원래 값을 집어넣어놓고 사용하는 과정이다. 버퍼의 값(원래값)을 꺼내서 마킹하는 식으로 초기 스냅샷 기준 마킹을 진행.
  2. Remark
    • STW
    • SATB Buffer 를 drain 하여 marking 마무리.
      • SATB Buffer 에는 변경 전 oops 가 들어있으며, 해당 oops marking 후 객체 그래프 탐색. 이 작업 반복.
    • 클래스 언로딩, 비어있는 Region 회수
    • Garbage 양에 따른 CSet Candidate 선정
    • TARS(Top-At-Rebuild-Start) 포인터를 Top 에 추가
  3. Concurrent Rebuild RSet, Scrub Region
    • CSet Candidate 에 속한 Old Gen Region 의 Region Bottom ~ TARS 사이 객체에 대해 cross-region 참조를 찾고 RSet 을 갱신
      • 이는 mixed gc 시 RSet 을 참고하여 GC 를 하기 위해 RSet 을 정비하는 작업
  4. Cleanup
    • STW
    • CSet Candidate 을 효율성 및 연결성 순으로 정렬하여 최종 확정
    • CSet 의 상태를 보고 Reclaimation Phase 를 아예 실행하지 않기로 결정할 수도 있음

가비지 컬렉팅

Young GC 와 Mixed GC 둘 다 크게 네가지 단계로 이뤄진다.
다른건 CSet 이 Young Gen Region 이냐, Young Gen Region + Old Gen Region 이냐다.

  1. Pre Evacuate Collection Set
    • TLAB 를 유저 스레드에서 해제
    • CSet candidate 에서 CSet 선택
  2. During Merge Heap Roots
    • CSet 의 RSet 을 병합. 여러 Region 을 처리할 것이기 때문에 중복을 제거해두기 위해.
  3. Evacuate Collection Set
    • 루트 참조로부터 객체 그래프 끝까지 스캔 및 복사/이동
      - 상위에서 참조된 객체는 살아있다는 것이고, 해당 객체를 객체를 복사/이동
      • 이후 해당 객체를 CSet Root 로 삼아서 하위 그래프 스캔
      • 더이상 하위 객체가 없을 때 까지 반복
  4. Post Evacuate Collection Set
    • Free CSet 등 정리 작업

Young GC

  • 대상은 Eden, Survivor 전체
  • Eden 의 객체를 Survivor 로 복사
  • Survivor 의 객체를 Old 로 복사
  • 죽은 객체는 CSet 에 추가되어 다음 Young GC 또는 Mixed GC 에서 정리

Young GC 에서 일시 정지 시간 목표 달성

최대 일시 정지 시간 설정인 -XX:MaxGCPauseTimeMillis 와 GC 간격인 -XX:GCPauseIntervalMillis 는 일시 정지 시간에 영향을 미칠 수 있다.

이 옵션으로 셋팅된 정지 시간 목표를 달성하기 위해 긴 시간 관찰된 비슷한 크기의 young gen 이 GC 되는데 걸린 시간, GC 과정에서 복사했던 객체의 수, 등등의 정보를 근거로 Young GC 가 끝날 때 마다 Young 영역의 크기를 다시 결정한다.

-XX:G1NewSizePercent=5, -XX:G1MaxNewSizePercent=60
이 두 사이의 값에서 G1 가 동적으로 Young Gen 의 크기를 결정한다. 값은 전체 힙 크기 중 Young Gen 의 비율이다.

이렇게 Young GC 는 Young Gen 의 크기를 매번 조정해가면서 목표한 일시 정지 시간을 맞추는데 노력한다.

Mixed GC

  • Space-Reclamation 의 목표는 mixed gc 를 통해 Old 영역의 크기를 줄이는 것
  • Mixed GC 는 Reclaimation Phase 에서 N 번으로 나뉘어 실행
    • XX:G1MixedGCCountTarget = 8 : 몇번 실행할지 결정
    • CSet Candidate 개수 / XX:G1MixedGCCountTarget = Mixed GC 에서 대략적으로 정리되는 Region 수
  • Young/Old 두 영역 모두의 객체를 대상으로 GC 한다고 해서 Mixed
  • Space-Reclamation 가 시작되면 Young Gen 의 크기는 -XX:G1NewSizePercent 값으로 고정.
  • 목표 정지 시간을 넘어서지 않는 선에서 CSet 순서대로 회수 Region 선택(마킹 단계에서 회수효율 순으로 정렬됨)
  • CSet Candidate 에 포함된 Region 은 Conc Marking 단계에서 -XX:G1MixedGCLiveThresholdPercent=85 옵션으로 필터링 되어 있는 상태. 즉, 15% 이상이 죽은 객체인 리전만 들어 있고, 그게 GC 대상 Region.
  • XX:G1HeapWastePercent=5 : space-reclamation 를 종료하는 트리거. 전체 힙 크기 대비 CSet 에 남은 미회수 공간 크기의 합의 비율이 이 값보다 낮다면 space-reclamation 를 중지. 쉽게 말하면, CSet 에 정리할 공간이 적게 남았다면 끝낸다.

Tams

  • Bottom ~ prev Tams : 이전 Marking 에서 확정된 Bitmap
  • prev Tams ~ next Tams : 이전 Marking 시작단계 ~ 현재 Marking 시작단계 까지 새로 생긴 객체들.
    • bottom ~ prev Tams 구간과 함깨 현재 marking 단계의 마킹 대상이 된다. (현재 마킹 이후는 Bottom ~ next Tams 까지의 비트맵이 생긴다)
  • next Tams ~ end : 이번 marking 단계 중 새로 생긴 객체들. alive 상태로 간주.
    마킹 단계에서는 현재 리전에 대한 마킹과 이전 리전 마킹 기록 efficency 계산이 동시에 이뤄진다. 따라서 두개의 포인터를 두고, 이전 마킹 단계에서 비트맵이 확정 된 곳이 어디까지인가의 저장과, 현재 마킹 단계 시작 이후 생긴 객체들은 어디부터인가를 같이 저장해야한다.
    단 이건 jdk20 에서 방식이 바뀌었다.
    비트맵에는 마킹 여부를 표시. 이후 객체 생사를 판단하고, 살아있는 객체가 얼마나인가(회수 가능한 객체가 얼마나인가) 판단에 쓰임.

write barrier

  1. RSet Card Marking Write Barrier(post write barrier) : 객체 참조 변경시 Rset 의 변경을 큐에 넣는 베리어. 한꺼번에 처리하기 위해 큐에 넣음.
  2. Concurrent Marking SATB Write Barrier(pre write barrier) : SATB 동작에서 객체 참조 변경(삭제)시 old ref(이전 값) 을 보존하기 위해 SATB Buffer 에 저장하는 베리어

기억집합(RSet)


https://www.alibabacloud.com/blog/a-detailed-explanation-of-jvm-garbage-collector-g1%26zgc_601536

기존의 Serial GC 에서의 Card Table 은 Old Gen 의 크기를 일정 크기 Page 로 나눈 뒤 해당 영역에 대한 Dirty Check 만 하는 Byte Array 이다. 이는 Young Gen 하나 Old Gen 하나의 구조이기 때문에 가능했다. Young Gen 에서 Dirty 표시를 하면 참조하고 있는 대상인 비회수영역은 Old Gen 하나밖에 없다는 말이다.

하지만 Region 여러개로 나뉜 G1 GC 에서는, 회수 대상 Region 의 객체를 참조하는 비회수 대상 Region 이 여러개일 수 있다. 따라서 G1 에서는 리전간 참조를 기억하기 위해 Remember Set(RSet) 이라는 새로운 구조를 만들어 사용한다.
G1 은 기존과 같이 (기본적으로 힙을 512 byte 단위로 나눠서) Card Table 을 관리한다. 다만 Region 마다 존재한다. RSet 은 카드 테이블에 dirty 된 영역이 어떤 외부 Region 으로부터의 참조 때문인지를 인덱스로 관리하는 것이다.

G1 은 Young-Only Phase 에서 Young Gen 에 있는 객체만 정리한다. 이 때 Old Gen Region 에서 참조하고 있는 객체는 정리하면 안된다. 또한 살아남은 객체를 복사 및 이동시에 Old Gen 의 참조를 변경시켜줘야 한다. 이럴 때를 위해 기억집합이 존재한다.

Mutate Thread(유저 스레드) 는 객체의 포인터를 변경(살아남은 객체 이동 등)시에 Post Write Barrier 로 Card Table 에 Dirty 표시를 하고, 해당 사실을 Log Buffer 에 저장한다. Concurrent Refinement Threads 는 버퍼에서 Log 를 꺼내 RSet 을 업데이트한다.
이런 지연된 구조는 한꺼번에 RSet 을 처리하여 효율을 높이기 위해서다.

Concurrent Marking 단계에서 RSet 을 최신화 시키는 단계가 있다.
가비지 컬렉션의 Merge Heap Roots 단계에서는 중복을 제거하기 위해 리전당 RSet 으로 부터 Global RSet 를 만드는 단계가 있다.
RSet 은 리전마다 저장하므로 메모리 소비가 G1 내부에서 큰 편이다. 특히 Young Gen 의 크기를 작게 설정할 수록 그만큼 Old Gen 으로 승격되는 객체가 많아져 RSet 의 크기가 커질 수 있음을 유의하자.

CSet

CSet 은 정리 및 복사(Evacuate) 할 대상 Region 의 집합.
CSet 은 GC 의 Pre Evacuate Collection 단계에서 정해진다.

Young GC : Young Gen 의 전체가 CSet 이 된다.
Mixed GC : Young Gen 전체 + Concurrent Marking 의 결과로 Mixed GC 의 pre-evacuation. 에서 만든 것.

Young GC 는 전체 Young Gen 이 CSet 이기 때문에 고르고 뭐고 하는 과정은 없다.

Mixed GC 에서 사용할 CSet 은 다음과 같이 정해진다. [참고] [참고]
1. Concurrent Marking의 Remark 에서 점유율이 낮은 영역, 즉 여유 공간이 많은 영역을 선택하여 CSet Candidate 을 선정.
2. Concurrent Marking 의 Clean Up 에서 CSet Region 의 점유율과 연결성을 고려하여 GC 효율 순으로 정렬하여 최종 CSet Candidate 를 만듦. (Refine)
3. Mixed GC 에서는 이 정렬된 CSet Candidate 중 N 개의 CSet 을 선택하여 정리. Mixed GC 는 Space Reclaimation 단계에서 여러번 실행될 수 있으며, 각각의 GC 에서 CSet Candidate 중 CSet 를 선택.

IHOP

Recaimation Phase 로 넘어가기 위해 Young-Only Phase 에서 Concurrent Marking 을 시작할 임계점.
-XX:InitiatingHeapOccupancyPercent=45 로 셋팅할 수 있다.
G1 은 Adaptive IHOP 기능을 지원한다. 처음 두세번의 Concurrent Marking 시작 임계점은 -XX:InitiatingHeapOccupancyPercent=45 으로 하되, 몇번의 GC Cycle 로 정보가 쌓인 후에는 처리량과 정지 시간에 알맞은 IHOP 를 자동으로 정해준다. -XX:+G1UseAdaptiveIHOP 로 켤 수 있다.

이렇게 G1 GC 가 자동으로 현재 상태에 맞게 자원이나 옵션을 자동으로 변경하는 것을 Ergonomic 이라 한다.

주기적 가비지 컬렉팅

운영체제에 힙을 할당/반환하는 과정은 동시 마킹의 Remark 또는 Full GC 의 STW 상태에서만 진행한다.

하지만 G1 은 Full GC 를 최대한 피하는 방식으로 설계되었고, 애플리케이션이 유휴상태라 새로운 객체가 생성되지 않아 동시 마킹이 시작되는 임계값(IHOP) 을 넘지 못한다면 운영체제에 반환할 수 있는 Heap 메모리가 있다 하더라도 반환하지 못하게 될 수 있다. 이런 현상은 On-Demand Cloud 환경에서 비용적으로 불리할 수 있다.

-XX:G1PeriodicGCInterval
-XX:G1PeriodicGCSystemLoadThreshold

거대 객체

리전의 크기의 반 이상 크기인 객체를 거대 객체(Humongous Objects
)라 한다.
거대 객체는 Old Gen 의 연속된 리전인 거대 리전에 저장된다. 만약 1.5개 리전의 크기의 객체라면 2개의 리전을 차지하며 나머지 0.5 Region 은 쓰이지 않는다.

이러한 거대 객체는 Concurrent Marking 의 CleanUp 또는 Full GC 에서만 Reclaim 될 수 있다. 다만 기본타입 거대 객체의 경우 GC Pause 도중 회수할 수 있다.

거대 리전은 연속된 거대한 Region 을 필요로 하기 때문에 Full GC 를 발생시킬 확률이 있다.
또한 거대 객체는 Full GC 중에도 Compaction 을 위해 이동되지 않는다. 따라서 거대 객체가 많아지면 파편화가 심해져 메모리 부족 오류를 만날 수 있다.
더욱이, 메모리 공간이 충분하더라도 연속된 공간을 찾지 못해 객체 할당 실패 오류를 겪을 가능성도 있다. 이럴때는 Region Size 를 늘려 Region 이 수용할 수 있는 크기를 늘리거나, 힙 자체를 늘리는 방법 밖에 없다.

gc+heap=info 옵션에서 거대 객체의 개수를 알 수 있다.

SATB

SATB 는 marking 을 위한 객체 그래프 스캐닝 중 유저 스레드에 의해 참조 삭제가 일어날 경우 그 사실을 기록한 후 나중에 적용한다. 즉, 마킹 시작시에 살아있던 객체는 끝까지 살아있는 것으로 간주한다.

mutator thread 가 만약 A.a = b 인 상태에서 A.a = c 로 참조 변경을 했다고 치자. 이 때 A.a -> b 로의 참조는 사라지게 되는 것이다.
이 때 Pre Write Barrier 가 동작하여 기존 A.a 에 들어있던 b 참조를 SATB Buffer 에 저장한다.

이후 SATB Buffer 에 있던 참조(b) 는 marking 단계에서 GC Root 로 등록된다.

A.a -> b 의 참조가 사라진 것인데, SATB Buffer 에는 b 값만 저장해도 될까? 어디서 온 참조인지는 저장하지 않아도 될까?
SATB 의 목적은 '시작단계에 살아있던건 살아있다 간주' 이기 때문에, 삭제 대상이던 b 가 살아있도록만 만들어주면 된다. b 를 GC Root 로 등록하면 b 부터 시작하여 하위의 참조 객체들이 다 살아남을 수 있다. 그렇기 때문에 SATB Buffer 에는 b 만 넣으면 된다.

SATB 는 같은 문제를 해결하는 incremental-update 방식에 비해 일시 정지 시간을 줄여줄 수 있다. Marking 과 동시에 수정되는 객체 참조의 유형은 대부분이 새로운 객체의 생성이기 때문이다.
삭제된 객체만 관심을 가지는 SATB 방식이 새로운 참조에 관심을 가지는 incremental-update 보다 할일이 적다. [참고 : Compile-Time Concurrent Marking Write Barrier Removal]

SATB 의 단점도 있다.
Concurrent Marking 의 초기 단계에서 SATB 를 확정하면 이후 Marking 이나 Mixed GC 는 모두 시작단계 스냅샷 기준으로 동작한다. 즉, SATB 직후에 죽은 객체가 있더라도 다음 사이클이 도래하여 SATB 를 다시 찍기 전에는 죽은걸로 인식되지 않는다.
이런 객체를 부유 쓰레기(floating garbage. 실제로 죽었는데 살아있는것 처럼 보여 회수되지 못함) 라 하며, 메모리 사용량을 높게 한다.

Full GC

Old Gen 의 힙 점유율이 높아 객체를 할당하지 못하면 Full GC 가 발생한다. 특히 거대 객체를 할당할 때 발생할 수 있다.

  • 애플리케이션이 회수할 수 없을 정도로 너무 많은 객체를 할당할 경우
    • Concurrent Marking 이 진행중일 때 계속해서 객체가 쌓이면 Mixed GC 에서 Old Gen 의 객체를 정리하기 전에 Full GC 가 발생해버릴 수 있음.
  • 거대 객체를 할당하는데 연속된 메모리 공간을 찾지 못할 경우

이러한 Full GC 는 긴 STW 를 발생시켜서, 최대한 줄이는 노력을 해야한다.
GC Log 에 Pause Full (Allocation Failure) 로 표시된다.

FUll GC 를 줄이기 위한 방법은 다음과 같다.
1. 거대 객체를 확인하고 리전 사이즈 늘리기
2. 힙 사이즈 늘리기
3. -XX:ConcGCThreads 로 동시 처리 스레드 늘리기
4. IHOP 수정으로 동시 마킹 더 빠르게 하기

Adaptive Option

G1 에는 Ergonomic 이라는 애플리케이션이 실행되며 자동으로 수정되는 값들이 있다. 이를 알아두면 좋을 것.

https://docs.oracle.com/en/java/javase/17/gctuning/garbage-first-g1-garbage-collector1`Ergonomic` 이라는 .html#GUID-082C967F-2DAC-4B59-8A81-0CEC6EEB9016

실제로 보기 옵션

-XX:+UseG1GC -XX:G1HeapRegionSize=1m -XX:InitiatingHeapOccupancyPercent=20 -XX:MaxGCPauseMillis=50 -Xlog:gc*=debug
Mixed GC 가 일어날 수 있게 Concurrent Marking 이 발생하는 임계값을 좀 낮춰준다.
-Xlog:gc 에는 ergo, heap, age... 많은 태그를 같이 쓸 수 있는데, 모두 볼 수 있게 * 와 레벨을 debug 로 해줄 수 있다.


참고

마킹 전체 내용 - https://tschatzl.github.io/2022/08/04/concurrent-marking.html
전체 내용 잘 설명 - https://www.alibabacloud.com/blog/a-detailed-explanation-of-jvm-garbage-collector-g1%26zgc_601536
oracle 문서, 버전마다 조금씩 다르다 - https://docs.oracle.com/en/java/javase/21/gctuning/garbage-first-g1-garbage-collector1.html#GUID-572C9203-AB27-46F1-9D33-42BA4F3C6BF3
redhat. GC 과정에 전체 리전이 어떻게 변화하는지 그림이 잘되어 있음 - https://www.redhat.com/en/blog/part-1-introduction-g1-garbage-collector
redhat. jdk8 기준으로 로그 보면서 설명. - https://www.redhat.com/en/blog/collecting-and-reading-g1-garbage-collector-logs-part-2

G1 논문. 기본적인 아이디어가 거의 다 설명되어 있음 - Garbage-First Garbage Collection. David Detlefs..

jdk20 bitmap 관련 변경. 이런 식으로 메모리 사용량이 줄고 있음. - https://github.com/openjdk/jdk/pull/8957

활동의 거의 없는 애플리케이션의 주기적 GC JEP - https://openjdk.org/jeps/346

RSet, Write Barrier 등 - https://www.jfokus.se/jfokus17/preso/Write-Barriers-in-Garbage-First-Garbage-Collector.pdf

그 외
1. https://www.oracle.com/technical-resources/articles/java/g1gc.html#Taming
2. https://tschatzl.github.io/2025/02/21/new-write-barriers.html
3. https://tschatzl.github.io/2021/02/26/early-prune.html

profile
안녕하세요!

0개의 댓글