[IT 심층 분석]
JVM 기반 서비스의 GC 중단 시간을 Linux Huge Pages로 극적으로 단축한 서버 튜닝 기록
김민준 · IT 시스템 엔지니어|
Java 기반의 고성능 트레이딩 시스템을 운영하면서 가장 고통스러운 적은 GC(Garbage Collection) 중단 현상이었습니다. G1GC 수집기를 사용하고 있었음에도 수십 기가바이트의 힙을 가진 JVM이 Full GC를 수행할 때면 수백 밀리초 동안 애플리케이션의 모든 스레드가 일시 정지하는 스탑-더-월드(Stop-the-World)가 발생해 트레이딩 체결 시스템의 레이턴시 요구사항을 정기적으로 위반하곤 했습니다. JVM 힙 설정과 GC 파라미터를 갖가지 방식으로 뜯어고쳐봤지만 근본적인 돌파구는 전혀 다른 방향에서 왔습니다.
힌트는 JVM의 GC 사이클이 빠르게 끝나려면 힙 메모리 전체를 순회하는 동작이 빨라야 한다는 사실에서 왔습니다. JVM이 힙을 쓸어가며 오래된 객체들을 표시하고 이동하는 과정에서 운영체제의 메모리 페이지 테이블을 무수히 많이 조회하게 됩니다. 기본 크기인 4KB 페이지를 사용하면 수십 GB 힙을 채우는 데 수천만 개의 페이지 테이블 엔트리가 필요하고 TLB(Translation Lookaside Buffer) 즉 가상-물리 주소 변환 캐시가 계속 미스를 내며 메모리 접근마다 불필요한 계층 탐색 오버헤드가 누적됩니다.
Huge Pages는 이 문제를 근본에서 다루는 비책입니다. 리눅스는 2MB 혹은 1GB 크기의 거대한 메모리 페이지를 지원하는데 똑같은 32GB 힙이라도 4KB 페이지 8백만 장이 아닌 2MB 페이지 16000장으로 나타낼 수 있게 됩니다. 이렇게 되면 TLB는 훨씬 적은 항목 수로도 동일한 메모리 공간을 커버할 수 있어 TLB 미스 발생 빈도가 극적으로 줄어들고 결과적으로 메모리 접근 속도가 향상됩니다. /etc/sysctl.conf에서 vm.nr_hugepages를 설정하여 충분한 크기의 Huge Page 풀을 확보하고 JVM 실행 시 -XX:+UseHugePages 또는 -XX:+UseLargePages 플래그를 추가하자 JVM이 자동으로 Huge Page 영역에 힙을 할당하게 되었습니다. 비교 테스트 결과 Full GC 중단 시간은 평균 680ms에서 140ms로 80% 이상 감소했습니다. 수십 킬로미터에 달하는 파라미터 튜닝 여정보다 운영체제의 메모리 시스템을 이해하는 단 한 가지 통찰이 더 극적인 결과를 만들어낼 수 있었습니다.