본문 바로가기

프로그래밍 언어/java

Java는 왜 컴파일러와 인터프리터를 같이 사용하는가?

배경

예전에 C를 배우고 Python을 배울 때, 약간 의문이 들었던 것이 있다.

❓왜 Python은 C랑 다르게 더 느린 Interpreter를 사용하지?

✅ Python의 설계 철학은 더 쉽고 더 용이한 개발을 추구하는 것이다.

Python이 Interpreter를 사용함으로 얻는 장점

  1. 컴파일 과정없이 코드를 빠르게 실행할 수 있어서 개발과 테스트를 빠르게 할 수 있음.
  2. 한 줄씩 실행하므로 디버깅이 용이함.
  3. 동적 타이핑(C나 Java처럼 타입이 정적으로 정해지지 않아도 되는것).
  4. 이식성이 높음.

위의 이유로 언어마다 추구하는 장점이 다른 것으로 이해하고 넘어갔다.

하지만 Java는 컴파일러와 인터프리터를 모두 사용한다.

Java 실행과정

java 실행과정을 간단히 살펴보면 아래와 같은 순서로 실행된다.

  1. 우선 Java 컴파일러(javac)가 개발자가 작성한 소스 코드(.java)를 바이트 코드(.class)로 컴파일 한다.
    -> 이때 바이트 코드는 플랫폼 독립적인 중간 형태의 코드이다. (= 기계어가 아님)
  2. 클래스로더가 실행에서 필요한 바이트코드를 메모리로 로드한다.
  3. 컴파일된 바이트코드는 Java Virtual Machine(JVM)에 의해서 해석되고 실행된다.
    • 처음 실행되는 코드들은 인터프리터에 의해 실행된다.
    • 만약 자주 실행되거나 최적화할 수 있는 코드가 있다면 JIT 컴파일러가 자주 실행되는 바이트코드 부분을 기계어로 컴파일하여 실행 속도를 높인다.

의문

❓ 자바는 왜 컴파일러와 인터프리터를 모두 사용하는가?

자바는 전체 코드를 바이트코드로 컴파일하는 과정이 있기 때문에, 인터프리터가 제공하는 빠른 실행의 장점을 잃는 것은 아닌가? 반대로, 인터프리터가 바이트코드를 한 줄씩 읽어 처리하기 때문에, 컴파일러의 성능 최적화 장점을 상실하는 것은 아닐까?

✅ 컴파일러 사용 이유

자바에서 개발자가 코드를 작성한 후 실행할 때는 컴파일과 인터프리팅이 모두 사용된다. 하지만 실제 운영 환경에서는 `.jar` 파일과 같은 빌드된 파일을 실행하기 때문에 컴파일 과정은 생략된다. 운영 환경에서는 이미 바이트코드로 변환된 파일을 `JVM`이 실행하므로, 인터프리터와 JIT(Just-In-Time) 컴파일러가 함께 동작하여 최적화된 성능을 제공한다.

  • 컴파일 단계에서는 소스 코드를 바이트코드로 변환하여, 문법 및 타입 오류를 사전에 검출한다. 이 과정은 빌드 시점에만 수행되므로 실행 시 오버헤드가 발생하지 않는다.

✅ 인터프리터 사용 이유

자바는 흔히 하이브리드 언어로 분류되지만, 실제 동작 시에는 인터프리터의 역할이 더 두드러진다. 실제 빌드된 바이트코드를 실행하는 방식이 인터프리터 방식이고 이는 인터프리터의 장점을 그대로 가져오게 된다. 또한, JIT 컴파일러를 통해서 최적화를 함으로써 자바의 인터프리터 특성을 보완하여 성능을 높이는 데 기여한다.

  • 인터프리터 단계에서는 `JVM`이 바이트코드를 읽어 실행하지만, JIT 컴파일러를 사용하여 핫스팟(자주 사용되는 메서드등)을 감지한 부분은 기계어로 변환하여 성능을 향상시킨다. 즉, 자바는 컴파일 언어의 사전 오류 검사 장점과 인터프리터 언어의 유연한 실행 장점을 모두 결합한 하이브리드 언어이다.

 

❓ 자바는 이제 (웹) 서버 이외의 용도로는 많이 사용되지 않는데, 굳이 이식성이 높을 필요가 있을까?

✅ 플랫폼 독립성의 중요성

자바의 플랫폼 독립성은 여전히 중요한 장점이다. 예를 들어, Windows 환경에서 빌드된 `.jar` 파일을 Linux 환경으로 옮겨 실행해도 문제없이 동작한다. 이는 자바가 플랫폼 간 호환성을 보장하기 때문에 다양한 운영 체제에서 동일한 코드를 활용할 수 있게 하는 것이다.

  • 특히 클라우드 기반 시스템에서 다양한 환경을 지원하는 것은 필수적이므로, 자바의 플랫폼 독립성은 여전히 중요한 가치로 남아 있다.