Safepoint와 Polling Page
Safepoint (안전지점) #
safepoint는 JVM에서 내부적인 전역작업 global operation을 수행하기 위해 실행중인 모든 java 스레드를 안전한 상태로 일시정지 시키는 지점(또는 그 기간)을 말한다.
흔히말하는 STW가 이 모든 스레드가 이 Safepoint에 도달해 멈춰있는 상태다.
왜 멈춰야할까 #
가장 대표적인 이유는 GC인데, GC가 힙메모리를 정리하거나 객체의 물리적 메모리 위치를 이동시키고 있는데 (relocation) 애플리케이션 스레드들이 멈추지 않고 예전 메모리 주소에 값을 쓴다면? 애플리케이션의 데이터가 완전히 박살날 것이다.
그 외에도 JIT 컴파일된 코드를 버리고 인터프리터로 돌아가는 Deoptimization, 전체 스레드의 Call Stack을 뽑아내는 Thread Dump등을 수행할 때 모든 스레드의 멈춤이 필요하다.
무엇이 안전한 것일까? #
os 레벨에서 스레드를 아무때나 강제로 정지시키면 안된다, 스레드가 멈춘 그 찰나의 순간에, cpu 레지스터나 스택이 들어있는 값들 중 어떤 값이 진짜 객체의 메모리 주소(포인터)이고, 어떤 값이 단순한 숫자(int, long) 데이터인지 JVM이 완전히 식별할 수 있어야한다.
이러한 포인터 매핑 정보(HotSpot에서는 이를 OopMap 이라고 함)가 완벽하게 준비되어 있어서, GC가 모든 참조를 헷갈리지 않고 정확히 추적할 수 있는 지점. 그곳에 스레드가 도달했을 때를 바로 Safepoint에 도달했다고 표현한다. 이때 멈추는것이 안전한 멈춤이다.
Polling Page #
그렇다면 JVM은 저 수많은 스레드들을 어떻게 억지로 끄지않고, 스스로 안전한 지점 safepoint에 멈추게 만들까?
여기서 등장하는것이 Polloing Page이다.
Polling Page의 본질은 safepoint 요청이 들어왔는지를 모든 스레드가 아주 적은 비용으로 수시로 확인(polling)할 수 잇도록 만들어준 전역 신호등 역할의 메모리 페이지(보통 4kb 크기)이다.
왜 필요하고 어떻게 동작하는 가 #
만약 스레드가 while(true) 같은 무한 루프를 돌면서 수학계산만 미친듯이 하고있다면, 외부에서 멈추라고 신호를 주지 않는 이상 영원히 safepoint에 도달할 수 없다.
따라서 JIT Compiler는 코드를 기계어로 번역할때 루프를 한 번 돌대마다 또는 메서드가 끝날때마다 혹시 지금 safepoint 요청이 왔는가? 라고 묻는 확인 코드 (Polling Code)를 삽입해둔다.
- 비효율적인 방법 (소프트웨어 검사): if (safepoint_requested == true) { 멈춤 }
- 이런 if문을 루프마다 넣으면 조건 분기 branch 때문에 프로그램 실행 속도가 느려진다.
- Hotspot의 우아한 방법 (polling page 활용)
- 평상시 jvm은 초기화 시점에 polling page라는 메모리 공간을 하나 만들고 읽기 가능 상태로 둔다
스레드들은 루프를 돌 때마다 이 페이지를 슬쩍 읽어본다(
Load명령어) 그냥 읽어지니까 아무문제 없이 초고속으로 하던일을 계속하고 오버헤드가 0에 가깝다 - Safepoint 발동시 jvm 마스터 스레드 VM Thread가 모든 스레드를 멈춰야할때 이 polling page os권한을 읽기 불가 PROT_NONE으로 닫아버린다.
- 애플리케이션 스레드들이 평소처럼 슬쩍 읽어보려다가 하드웨어 레벨에서 접근할 수 없는 메모리라는 Page Fault
SIGSEGV라는 에러가 터진다. - 이 에러를 os가 jvm에 알려주면 JVM C++ 시그널 핸들러가, 이건 진짜 에러가 아니라 safepoint 정지 신호구나 하고 해당 스레드를 안전하게 블로킹(대기) 시켜버린다.
- 평상시 jvm은 초기화 시점에 polling page라는 메모리 공간을 하나 만들고 읽기 가능 상태로 둔다
스레드들은 루프를 돌 때마다 이 페이지를 슬쩍 읽어본다(
요약하면 safepoint는 jvm이 내부 작업을 위해 스레드 상태를 정확히 파악할 수 있는 완벽한 상태의 멈춤이라는 목적이다.
polling page는 스레드들이 실행 흐름에 방해받지 않으면서도 jvm이 원할때 즉각적으로 그 목적지로 굴러떨어지도록 유도하는 가장 빠르고 최적화된 하드웨어 기반의 함정(트리거)이다.