어플리케이션의 초기 응답시간을 늘리기 위해 실제 API를 호출하는 웜업코드를 넣는다. 그러면 JVM웜업 및 컴파일러 캐싱이 동작하여 해당 API에 대한 다음 요청은 더욱 더 빨라진다. 해당 과정은 어떤 과정을 통해 진행될까? 이를 알기 위해서는 일단 JVM위에서 어플리케이션이 동작하는 방식에 대해 먼저 알아야 한다.
JVM은 환경에 독립적인 특징을 가지고 있다. 이것이 가능한 이유는 각 언어의 컴파일러가 이에 맞게 자바 바이트코드를 생성하기 때문이다.
아래의 예시를 보자.

코틀린 컴파일러는 개발자가 작성한 코틀린 코드를 컴파일하여 자바 바이트 코드를 만들어내고, 자바 컴파일러는 자바 코드를 컴파일하여 자바 바이트코드를 생성한다.

이렇게 생성된 코드는 ClassLoader가 클래스파일을 로드하여 메모리 영역에 배치한다. (클래스 로더는 JVM이 클래스 파일을 메모리에 로드하고, 실행할 준비를 할 수 있도록 한다.) 자바는 동적 로딩을 지원하기 때문에, 프로그램 실행 중에도 새로운 클래스를 로드할 수 있다.
클래스로더가 동적으로 클래스를 로드할 때 자주 사용되는 클래스 및 메소드에 캐싱이 적용된다.
Bootstrap Class Loader 이다. 클래스를 로드 할때는 부모부터 자식순으로 로드된다.이런
메소드 캐싱은 JVM 구현체에 따라 캐싱 알고리즘이 다를 수 있다. OpenJDK와 Oracle HotSpot JVM은 보통 LRU 기반의 캐싱을 사용하고, 대부분의 다른 JVM도 LRU를 채택하고 있다.
이후 메모리에 로드된 클래스파일을 이를 번역하여 기계어로만든다. 이런 기계어는 CPU에서 처리되게 된다. 이렇게 JVM은 컴파일 -> 인터프리터라는 2가지 동작에 대해 실행된다.
그러다 보니 컴파일과정에서 기계어를 만드는 C++, 고랭과 같은 타언어들과 비교해 성능이 떨어지게 된다.
이에따라 JIT Compiler가 생성되게 된다.
JIT 컴파일은 자바 프로그램이 실행될 때, 적시에 바이트코드를 기계어로 변환한다.
장점은 아래와 같다.
PGO 최적화)이것이 JVM이 HotspotVM 이라고 불리는 이유기도 하다. 핫스팟 == (자주 호출되고 실행되는 영역)
단점은 아래와 같다.
따라서 JIT Compiler는 프로필 기반 최적화(PGO - Profile guided optimization)를 제공한다.

여기서 JVM의 컴파일 단위는 메서드이므로, 메서드의 역할을 잘 구분지어 설계해둔다면 자주 호출되는 메서드가 명확해지므로 최적화할 때 훨씬 간편해진다고 한다.
Android Nugat(7.0) 버전 이후에는 AOT 컴파이일러와 더불어 JIT 컴파일러까지 탑재하여 혼용으로 사용한다.
인이는 최초 설치시에는 JIT를 사용하여 설치 속도를 높이고 차후 자주 사용되는 앱의 컴파일을 조금씩 하여 AOT 방식으로 전환한다. 즉, 각각의 컴파일 방식의 장,단점을 해소하기 위해 이렇게 컴파일을 2번 진행다.

아래는 카카오 T 계정 서버의 웜업과정의 예시이다.

Application이 시작되기 이전에 컴파일러 캐싱을 등록하기 위해 JVM warm up을 추가한다.
JVM warm up을 진행하는 방법으로는 2가지가 있을 수 있다.
ApplicationRunner를 통해 어플리케이션이 뛰어졌을 때 웜업을 수행한다.k8s환경 일때는 Liveness, Readiness 프로프를 요청할 때 웜업을 수행한다.readiness probe를 on으로 바꾼다.
카카오팀은 Liveness, Readiness요청이 모두 만족하면 3초뒤에 유저 트래픽을 인입하도록 설정하였다.
Liveness,Readiness를 통해 초기 웜업을 진행하는 것은 괜찮지만, 이는 추후 파드의 상태를 모니터링하도록 30초마다 호출되게 된다. "웜업이 진행이 완료되었다."를 30초 마다 체크하는 것 보다는,ApplicatinoRunner를 통해 초기 웜업을 진행하고initialDelay를 통해Readiness요청을 늦추는 방식도 좋을 것 같다.

카카오팀의 웜업의 동작 방식은 실제 로직과 동일한 요청으로 localhost를 호출하는 코드로 진행하였다. 지연이 되는 모든 API 요청를 웜업의 코드 동작으로 포함시켰다고 한다.
초기 tomcat, netty의 초기 핸들러 쓰레드 수를 배포가 된 이후의 쓰레드 수로 변경하여 쓰레드생성 비용을 절약한다.
데이터 베이스 커넥션도 똑같이 배포가 된 이후 안정적인 상태의 커넥션 수를 유지하도록 변경한다.
위의 방식으로도 웜업이 제대로 동작하지 않는 경험을 했다고 한다. 따라서 여러 Ideation을 거쳐 택한것은 바로 Warm up Count의 증가이다.

이 방식을 통해 초기 지연을 줄일 수 있었다고 한다.
이는
JIT Compiler의 PGO최적화 기반으로 작동하기에 웜업 카운트를 증가시키는 것 만으로도 초기 지연을 줄일 수 있다고 한다.
카카오 팀이 JVM기본 옵션을 활용하였을 때 시간대비 효율적이라고 판단한 횟수는 250회이다. (tps: 5000)
해당 프로필을 통한 구체적인 최적화 내용은 아래의 Tiered Compilation을 참고하라.
