트러블슈팅-warm up

왜 처음 켰을 때엔 응답이 느린가?

이를 알기 위해선 JVM의 작동 방식을 먼저 알아야 한다. JVM은 다음의 순서로 작동한다.

자바 소스코드(.java) ↓ // javac 컴파일 바이트코드(.class) ↓ // JVM 로딩 JVM 메모리에 적재 ↓ // 실행 방식 선택 [인터프리터 실행] 또는 [JIT 컴파일 → 네이티브 코드 실행]

바이트 코드

0: iload_1 // 첫 번째 매개변수를 스택에 로드 1: iload_2 // 두 번째 매개변수를 스택에 로드 2: iadd // 두 값을 더함 3: ireturn // 결과 반환

와 같은 어셈블리에 가까운 low level 언어이다. 이 언어들을 JVM의 인터프리터가 한 줄씩 읽고 기계어로 변환해서 실행한다.

그러나 메서드 호출 횟수가 특정 임계점을 넘기면 해당 구간은 핫스팟으로 판정되어 JIT 컴파일러가 처리하게 된다. JIT 컴파일러는 자주 사용되는 핫스팟의 바이트코드를 CPU에서 직접 실행할 수 있는 기계어인 네이티브 코드로 변환, 메모리에 저장하고 직접 실행한다.

따라서 한번 컴파일한 핫스팟의 코드는 실행이 매우 빨라지게 되는데, 이 과정을 워밍업이라고 한다.

그리고 여기서 문제가 발생하게 되는데, 현재의 프로젝트 구조는 9개의 스프링 컨테이너를 nginx를 통해 받는 형식이다. 그리고 스프링 인스턴스가 9개이기 때문에 워밍업 뿐만 아니라 커넥션 풀 초기화, 클래스 로드와 같은 초기 작업들을 각각 실행하게 된다.

그러므로 요청을 보내 초기 작업을 끝내고 응답을 받아도, 다음 요청에서는 초기 작업이 끝나지 않은 다른 인스턴스에 요청을 보내 또 느릴 수도 있는 것이다. 요청 수가 많지 않은 테스트 환경에서 이 문제는 더욱 두드러진다.

자주 껐다 켜지는 어플리케이션이라면 이러한 워밍업 부분도 최적화를 해야 하겠지만, 실제론 개발 환경과 테스트 환경을 구분하면 될 것 같고, 왜 실무에서 테스트와 배포 환경을 구분하는지 이유를 하나 더 알 수 있었다.