.class
파일로 변환한다.JIT 컴파일러
가 개입하게 된다.JIT 컴파일러는 실행 중(Run TIme)에 일부 코드를 네이티브 코드로 변환하여 최적화시킨다.
“변환한 네이티브 코드는 프로그램이 종료되면 사라진다. JIT는 네이티브 코드를 디스크에 저장하지 않고 메모리에만 저장하기 때문인데, 왜 그럴까?”
JIT는 실행 중에 성능을 분석하면서 최적화를 동적으로 조정할 수 있는데, 디스크에 네이티브 코드를 저장하게 되면 이러한 조정이 불가능해지기 때문이다.
예를 들어, 인라이닝 기법을 사용하다가 실행 도중에 사용을 중지할 수 있다.
JIT는 반복적으로 호출되는 메서드에 대해서, 메서드 호출을 제거하고 메서드 내용을 직접 삽입하여 최적화 하는 인라이닝 기법을 사용한다.
public static int calculate(int x) {
return x * 2;
}
int result = calculate(i); // 매번 메서드 호출
int result = i * 2; // 메서드 호출이 사라지고 직접 연산
컴파일 과정은 맞으나, C/C++과 같은 전통적인 방식과 다르게 전체를 한 번에 변환하는 것이 아니라 실행 중 일부만 변환하기 때문에 이를 동적 컴파일(Dynamic Compliation)이라고도 부른다.
public class JITExample {
public static void main(String[] args) {
System.out.println("Hello, Java!"); // ✅ 실행 1번 (JIT 대상 X)
for (int i = 0; i < 10000; i++) { // 🔥 핫스팟 (JIT 변환 O)
calculate(i);
}
printMessage(); // ✅ 실행 1번 (JIT 대상 X)
}
public static void calculate(int x) {
int result = x * 2;
}
public static void printMessage() {
System.out.println("This is a message!");
}
}
“왜 모든 코드를 JIT으로 컴파일하지 않을까?”
메모리 사용 문제
컴파일 시간 오버헤드
실행 방식의 유연성
- JIT의 동적 컴파일과 인터프리터의 빠른 첫 실행의 이점을 동시에 취할 수 있다.
⇒ 즉, JIT과 인터프리터가 공존하는 이유는 성능 최적화와 메모리 절약을 동시에 하기 위해서이다.
결론적으로, Java는 바이트코드를 실행할 때 인터프리터 방식과 JIT 컴파일러가 공존하는 하이브리드 언어이다.
이전부터 Python은 인터프리터, Java는 컴파일 언어라는 생각을 가지고 그렇게 배웠었는데, 자세히 파고드니 그렇지 않다는 것을 알게 되어 유익했다.
Java 파일이 컴파일 되고 해석되며 최종적으로 실행하는 일련의 과정을 알 수 있었다. 또한 JIT 컴파일러, 동적 컴파일에 대한 지식도 습득할 수 있었다.
.class
파일을 한 번 까보면 재미있을 것 같다는 생각이 든다.