스왑으로 발생한 Go 가비지 컬렉터의 40ms 정지

1 day ago 4

Go GC가 전체 실행 정지(STW) 중 읽는 힙 외부 메타데이터도 스왑으로 밀려날 수 있으며, 이를 다시 읽는 과정에서 최대 40ms 정지가 발생함 커널 6.8과 MGLRU를 사용한 Hetzner 실험에서 정지 시간 중앙값은 약 51µs였지만, 최악의 정지에서는 228회 페이지 폴트에 약 39ms를 소비함 메타데이터를 읽는 동안 모든 P가 정지하므로, 해당 시점에 I/O가 완료돼도 고루틴이 이를 처리할 수 없음 511KiB 메시지 생성도 평소 3~5ms에서 NVMe에서는 105ms, 네트워크 볼륨에서는 903ms로 늘어남. 원인은 확인되지 않았으며, 이 지연은 할당을 수행하는 고루틴에 국한됨 Go 1.26의 Green Tea GC에서도 측정 결과 메타데이터 읽기 문제에 대한 영향은 미미했음 스왑된 GC 메타데이터가 전체 실행을 멈추는 과정 메모리 급증을 흡수하기 위해 프로덕션 스왑 사용을 검토하며, 하나의 cgroup에 두 프로세스를 둔 구성을 실험함 Go 프로세스는 io.ReadAll 다음 proto.Unmarshal을 호출해 바이트 데이터와 그래프 구조체를 만들며, 그래프 구조체는 Go 할당기에서 scan으로 표시됨 다른 프로세스는 대부분 유휴 상태인 HTTP 서버임 GC는 scan 스팬을 포인터 단위로 읽음. 페이지 퇴거가 프로세스가 아닌 cgroup 단위로 이루어져 두 프로세스의 페이지가 함께 밀려나므로, 커널과 GC 사이의 반복적인 스왑 입출력 가능성이 작을 것으로 예상했지만 실험은 달랐음 GC는 STW 중 힙 외부 메타데이터를 읽으며, 이 영역은 해제되지 않고 재사용되지만 스왑 대상이 될 수 있음 런타임이 할당한 메타데이터 페이지는 GC 주기마다 읽힘 커널은 페이지의 접근 시점을 기준으로 오래 접근하지 않은 페이지를 스왑으로 내보냄 이후 GC가 전체 실행을 멈추고 해당 페이지를 읽으면 메이저 페이지 폴트가 발생함 페이지를 복구하려면 커널의 스왑 처리와 디스크 대기를 거쳐야 함 PTE 읽기, do_swap_page 호출, 새 프레임 확보, cgroup에 메모리 사용량 부과가 필요함 페이지 읽기, bio 제출, 디스크 대기, 메모리 복귀가 이어짐 Go GC는 스윕 종료 와 마킹 종료 두 지점에서 전체 실행을 정지함 이때 모든 P가 정지하므로, I/O를 기다리던 고루틴은 I/O가 완료돼도 정지 중에는 이를 처리할 수 없음 측정 결과와 확인되지 않은 비용 커널 6.8, MGLRU 활성화 상태의 Hetzne...

Read Entire Article