JIT 컴파일러가 뭔가요? 왜 우리 프로그램이 더 빨라지는 걸까요?
0. JIT 컴파일러, 자바 실행의 비밀
프로그래밍을 처음 배울 때 이런 의문을 가져보신 적 있나요? “내가 작성한 Java 코드가 어떻게 컴퓨터에서 실행되는 거지?” 오늘은 그 비밀을 풀어보겠습니다. 특히 우리가 모르는 사이에 프로그램을 더 빠르게 만들어주는 JIT 컴파일러
라는 마법같은 기술에 대해 알아보겠습니다.
1. JIT 컴파일러가 뭐예요?
먼저 컴파일러의 종류를 알아봅시다
프로그램이 실행되는 방식을 이해하려면 세 가지 방식을 알아야 합니다.
인터프리터 방식
카페에서 통역사가 실시간으로 대화를 번역해주는 것과 같습니다. 코드를 한 줄씩 읽으면서 바로바로 실행합니다. Python이나 JavaScript가 대표적인 예입니다. 장점은 코드를 바로 실행할 수 있다는 것이고, 단점은 매번 번역하느라 느리다는 것입니다.
AOT (Ahead-Of-Time) 컴파일러
책을 통째로 다른 언어로 번역해놓는 것과 같습니다. 프로그램을 실행하기 전에 미리 모든 코드를 컴퓨터가 이해할 수 있는 기계어로 바꿔놓습니다. C나 C++이 이런 방식입니다. 한 번 번역해놓으면 빠르게 실행되지만, 다른 컴퓨터에서는 다시 번역해야 할 수 있습니다.
JIT (Just-In-Time) 컴파일러
이름 그대로 ‘딱 필요할 때’ 번역하는 방식입니다. 프로그램이 실행되는 중에 자주 사용되는 부분을 발견하면, 그 부분만 골라서 빠른 기계어로 번역해놓습니다. 똑똑한 통역사가 자주 나오는 표현은 미리 외워두고 빠르게 말해주는 것과 비슷합니다.
Just-In-Time
의 진짜 의미
Just-In-Time
이라는 말은 “딱 필요한 순간에”라는 뜻입니다. JIT 컴파일러는 프로그램을 실행하면서 “아, 이 부분이 자주 실행되네?“라고 판단되는 순간, 그때서야 그 부분을 최적화된 기계어로 바꿔놓습니다. 마치 자주 가는 길을 외워서 더 빠르게 갈 수 있게 되는 것처럼요.
2. Java는 이미 컴파일하는데, 왜 또 컴파일할까요?
Java를 처음 배울 때 이런 경험 있으시죠? .java
파일을 작성하고 javac
명령어로 컴파일하면 .class
파일이 생깁니다. “어? 벌써 컴파일했는데 왜 JIT에서 또 컴파일한다는 거지?”
javac가 만드는 것은 ‘중간 언어’
사실 javac
가 만드는 .class
파일 안에는 ‘바이트코드’라는 것이 들어있습니다. 바이트코드는 컴퓨터의 CPU가 직접 이해할 수 있는 언어가 아닙니다.
이걸 비유로 설명하면:
- Java 코드 = 한국어로 쓴 편지
- 바이트코드 = 영어로 번역한 편지 (전 세계 어디서든 읽을 수 있지만, 아직 각 지역 방언으로는 번역 안 됨)
- 기계어 = 각 지역의 방언으로 번역한 편지 (그 지역 사람들이 가장 빠르게 이해할 수 있음)
JIT이 런타임에서 하는 일
JVM(Java Virtual Machine)이 바이트코드를 실행할 때, 처음에는 인터프리터처럼 한 줄씩 번역해서 실행합니다. 그러다가 “어? 이 코드가 100번째 실행되네?“라고 판단되면, JIT 컴파일러가 나서서 그 부분을 해당 컴퓨터에 최적화된 기계어로 번역해놓습니다.
3. WAR/JAR 안에는 무엇이 들어있나요?
웹 개발을 하다 보면 WAR 파일이나 JAR 파일을 자주 보게 됩니다. 이 파일들 안에는 무엇이 들어있을까요?
바이트코드가 가득한 압축 파일
WAR/JAR 파일은 사실 ZIP 파일과 같은 압축 파일입니다. 안에는:
- 컴파일된
.class
파일들 (바이트코드) - 설정 파일들
- 라이브러리들
- 웹 리소스들 (HTML, CSS, JavaScript 등)
이 모든 것들이 하나의 파일로 묶여있어서 배포하기 편합니다.
실행 과정: WAR/JAR → JVM → JIT → CPU
- WAR/JAR 파일 실행: 톰캣 같은 서버나
java -jar
명령어로 실행 - JVM이 바이트코드 로딩: 압축을 풀고 필요한 클래스들의 바이트코드를 메모리에 올림
- 처음에는 인터프리터로 실행: 바이트코드를 한 줄씩 번역해서 실행
- JIT이 개입: 자주 실행되는 코드를 발견하면 기계어로 컴파일해서 저장
- 다음부터는 기계어로 직접 실행: 훨씬 빠른 속도로 실행
4. JIT을 쓰면 뭐가 좋아질까요?
실행 속도가 점점 빨라집니다
JIT의 가장 큰 장점은 프로그램이 오래 실행될수록 점점 빨라진다는 것입니다. 마치 새로운 게임을 처음 할 때는 서툴지만, 계속 하다보면 실력이 늘어서 더 빠르게 클리어할 수 있게 되는 것과 같습니다.
핫스팟 최적화
JIT은 “핫스팟”이라는 것을 찾습니다. 이는 자주 실행되어서 “뜨거워진” 코드 부분을 의미합니다. 예를 들어 반복문이 1000번 돌아가는 코드가 있다면, JIT은 “아, 이 부분이 핫스팟이네!“라고 판단하고 최적화합니다.
인라인 최적화
함수 호출을 줄이는 최적화도 합니다. 원래는 함수를 부르고 결과를 받아오는 과정이 있었다면, JIT은 아예 함수 내용을 그 자리에 직접 넣어버립니다. 마치 “더하기 함수 부르지 말고 그냥 여기서 바로 더해버리자”는 식으로요.
루프 최적화
반복문도 더 효율적으로 만듭니다. 불필요한 검사를 줄이거나, 여러 번 반복되는 계산을 미리 해놓기도 합니다.
자주 쓰는 코드는 캐시에 저장
JIT은 최적화한 기계어 코드를 메모리에 저장해둡니다. 다음에 같은 코드가 실행될 때는 저장된 기계어를 바로 사용하므로 훨씬 빠릅니다. 마치 자주 먹는 라면을 미리 끓여두고 데워먹는 것과 비슷합니다.
5. 우리가 모르는 사이에 이미 사용 중인 JIT
대부분의 현대 언어가 JIT 사용
놀랍게도 우리가 자주 사용하는 많은 언어들이 JIT을 사용합니다:
- Java: HotSpot JVM이나 OpenJ9 같은 JVM들이 JIT 컴파일러를 내장
- .NET (C#, VB.NET): .NET Runtime이 JIT 컴파일러 사용
- JavaScript: Chrome의 V8 엔진, Firefox의 SpiderMonkey 등이 JIT 사용
- Python: PyPy라는 구현체에서 JIT 사용
이 모든 언어에서 JIT이 기본적으로 켜져있어서, 우리는 모르는 사이에 그 혜택을 누리고 있는 겁니다.
JIT을 끄는 경우는 언제일까요?
보통은 JIT을 끄지 않지만, 가끔 끄는 경우가 있습니다:
-
메모리가 극도로 제한된 환경
JIT은 최적화된 코드를 저장하기 위해 추가 메모리를 사용합니다. IoT 기기처럼 메모리가 매우 적은 환경에서는 끄기도 합니다. -
예측 가능한 성능이 중요한 경우
JIT은 최적화 과정에서 잠깐 성능이 떨어질 수 있습니다. 실시간 시스템처럼 성능 변동이 없어야 하는 경우에는 끄기도 합니다. -
매우 짧게 실행되는 프로그램
JIT이 최적화할 시간도 없이 금방 끝나는 프로그램에서는 오히려 JIT이 오버헤드가 될 수 있습니다.
6. 마무리
JIT 컴파일러는 현대 프로그래밍에서 없어서는 안 될 기술입니다. 우리가 의식하지 못하는 사이에 프로그램을 더 빠르게 만들어주고, 다양한 환경에서 최적화된 성능을 제공해줍니다.
다음번에 Java나 JavaScript로 개발할 때, “아, 지금 JIT이 내 코드를 더 빠르게 만들어주고 있구나!“라고 생각해보세요. 여러분의 프로그램 뒤에서 열심히 일하고 있는 JIT 컴파일러에게 감사의 마음을 가져보는 것도 좋을 것 같습니다.
Enjoy Reading This Article?
Here are some more articles you might like to read next: