728x90
반응형
힙 크기가 무관하게 일시정지 시간을 1ms 미만으로 보장하는 것이 목표
참고로 얘는 64비트 이상의 컴퓨터에서만 활용 가능하다(컬러드 포인터의 동작 방식과 연관이 있음)
개념
컬러드 포인터, 로드 베리어라는 것이 핵심이다.
- 컬러드 포인터(Colored Pointer)
- 64비트 운영체제에서 메모리 주소는 64비트를 다 사용하지 않는데(64비트는 진짜 개커서 실제로 컴퓨터는 48/52비트를 사용하도록 설계되어 있기 때문) 그래서 나머지 비트는 잉여 공간이 된다.
- 이렇게 남은 잉여 공간 활용 가능
- GC 관련 메타데이터(객체가 마킹되었는지, 재배치 되었는지 등)을 저장
- 헤더가 아닌 포인터 자체에 정보를 저장하기 때문에 객체에 접근하는 시점에 상태를 알 수 있다
- 64비트 운영체제에서 메모리 주소는 64비트를 다 사용하지 않는데(64비트는 진짜 개커서 실제로 컴퓨터는 48/52비트를 사용하도록 설계되어 있기 때문) 그래서 나머지 비트는 잉여 공간이 된다.
- 로드 배리어
- GC에서 객체 이동을 하면 파편화가 발생할 수 있어(주소의 변경을 모든 곳에 해줘야 하므로) 이 때 STW 가 발생했다.
- 객체 이동 시점에 STW 를 안하고 애플리케이션 스레드가 힙에서 객체 주소를 읽으려 하면 로드 배리어 실행
- 위의 컬러드 포인터로 마킹한 색을 확인하고, 객체가 이동되었다면 주소를 변경하고 데이터를 돌려줌
- 이를 통해 ZGC는 객체 이동 도중에도 애플리케이션을 멈추지 않는다.
- 위의 컬러드 포인터로 마킹한 색을 확인하고, 객체가 이동되었다면 주소를 변경하고 데이터를 돌려줌
- 객체 이동 시점에 STW 를 안하고 애플리케이션 스레드가 힙에서 객체 주소를 읽으려 하면 로드 배리어 실행
- GC에서 객체 이동을 하면 파편화가 발생할 수 있어(주소의 변경을 모든 곳에 해줘야 하므로) 이 때 STW 가 발생했다.
동작
위의 내용을 통해 ZGC 에서 객체 이동 과정에서는 STW가 발생하지 않는다는 것을 알 수 있었다.
그러면 어떨 때에 STW가 발생하는지, 그리고 왜 이게 힙 크기에 비례하지 않고 매우 짧게 유지할 수 있는지는 동작 방식을 통해 알아볼 수 있다.
이거는 여러 사이클로 구성되어 있다.
- Pause Mark Start (STW 발생)
- GC Root 에서 직접 참조하는 객체를 마킹한다.
- Concurrent Mark & Relocate (STW 없음)
- 위의 Pause Mark Start 단계에서 찾은 객체로부터 참조하는 모든 객체를 마킹한다.
- Relocation Set Selection
- 마킹이 끝나고 어떤 페이지에 살아있는 객체가 적은지(즉 비우는 것이 더 효율적인 곳인지) 결정한다.
- Pause Mark End (STW 발생)
- Concurrent Mark 가 마무리되고 동기화 되는 시점
- 새로 생성되거나 참조가 변경된 객체들을 최종적으로 처리
- G1GC 의 remark 와 비슷한 역할을 하고, 위에서 대상 객체들을 이미 줄여뒀기 때문에 빠르게 진행된다.
- Concurrent Relocate (STW 없음)
- 이게 핵심이다.
- 이제 Relocation Set 의 살아있는 객체를 새 페이지로 옮기는데(압축) 위의 로드 배리어를 활용해서 동작한다.
- 애플리케이션 스레드가 아직 옮겨지지 않은 객체에 접근하려 하면 ZGC가 그 객체를 바로 옮기고 변경된 주소를 반환한다.
- 애플리케이션 스레드가 이미 옮겨진 객체의 옛 주소로 접근하려 하면 로드 배리어가 이를 감지하고 포인터의 색을 활용해 새 주소를 찾아 반환해 준다.
- 위의 방식을 통해 객체가 이동 중인데도 외부 호출 시 올바른 객체로 접근 가능하다.
- Concurrent Remap (STW 없음)
- 모든 객체의 참조를 최종적으로 새 주소로 업데이트한다.
- Concurrent Relocate 에서 객체 이동은 끝났기 때문에 이제 옛 주소를 가리키는 포인터를 모두 새 주소로 매핑해준다.
장점
- 객체 이동 단계에서 STW 가 일어나지 않는다.
- 이 때 가장 긴 시간 STW가 일어나는데 얘는 그게 없으므로 일시정지 시간이 매우 짧다.
- 컬러드 포인터는 객체 헤더가 아니라 포인터의 미사용 비트에 메타데이터를 저장하기 때문에 메모리 오버헤드를 줄여줄 수 있다.
단점
- 처리량 감소
- 로드 배리어가 참조를 읽을 때 마다 실행되기 때문에 CPU 오버헤드가 지속적으로 발생한다.
- CPU, 메모리 사용량 증가
- GC 과정에서 계속 백그라운드 스레드를 사용할 수밖에 없다.
- Relocation Sets 같은 ZGC용 자료구조가 존재해야 하기 때문에 메모리 오버헤드 증가
- 64bit 이상에서만 가능(단점보다는 제약인듯)
얘는 결국, 컴퓨터의 성능이 일정 이상으로 좋은 상태 + STW 가 없으면 좋겠는 상황일 때 쓸만한 것 같다.
즉 배치 작업이나 별로 안좋은 서버에서 쓰면 큰일나는 친구라는 느낌
반응형
'이론 정리 > java' 카테고리의 다른 글
JDK LTS 와 각각의 내용에 대해 간략히 알아보자(JDK8, JDK11, JDK17, JDK21, JDK25) (0) | 2025.10.10 |
---|---|
간략한 Shenandoah GC 방식 설명 (0) | 2025.10.10 |
간략한 G1GC 방식 설명 (0) | 2025.10.10 |
간략한 Serial, Parellal GC 방식 설명 (0) | 2025.10.09 |
모던 자바 인 액션 4장 간략정리 (2) | 2024.11.26 |