[CS] 컴파일 언어 VS 인터프리터 언어, 그리고 Java

컴파일 언어와 인터프리터에 관한 내용입니다.


컴파일 언어 VS 인터프리터 언어

  • 항상 접할때마다 헷갈려서 정리하기로 했다.

컴파일 언어

  • 한번에 전체 코드 번역
    • 소스코드를 기계어로 변환하는 과정(컴파일)을 거친 후 실행
    • 실행 파일이 생성됨으로 배포에 용이함
  • 컴파일 타임 사용
    • 이 과정을 거쳐 소스코드는 기계어가 되어 실행가능한 상태가 됨
  • 실행 속도
    • 기계어로 변환된 상태에서 실행되므로 매우 빠름
    • 인터프리터 언어보다 20~100배 이상 빠름
  • 개발 과정
    • 문법 오류를 실행 전에 발견할 수 있어 안정적임
    • 하지만 코드 변경 시 다시 컴파일해야 하므로 개발 편의성이 다소 떨어짐
  • 대표적인 언어
    • C, C++, 러스트, Go 등

인터프리터 언어

  • 스크립트 언어
    • 별도의 실행 파일 없이, 소스코드를 직접 실행하는 경우가 많음
  • 번역과 실행이 동시에 이루어짐
    • 프로그램을 실행할 때 한 줄씩 읽고 해석한 뒤 바로 실행
  • 런타임 사용
    • 실행 시점에 코드가 해석되므로 빌드 과정이 필요 없음
  • 실행 속도
    • 한 줄씩 해석하며 실행하므로 컴파일언어보다 느림.
  • 개발 과정
    • 즉시 실행 및 테스트가 가능하여 개발 속도가 빠름
    • 동적 타입을 지원하는 경우가 많아 코드 수정이 용이
  • 대표적인 언어
    • Python, Ruby, JavaScript 등
  • 기타 특징
    • python은 C++로 만들어져 있음
      • 컴파일 언어는 빠르지만 개발 편의성이 떨어져 인터프리터 언어를 만드는데 사용되기도 함
    • 실행 속도를 개선하기 위해 JIT(Just-In-Time) 컴파일러를 사용하는 경우도 있음
      • Python의 PyPy, Javascript의 V8엔진 등

왜 인터프리터 언어가 더 느릴까?

  • 인터프리터 언어가 빌드 과정이 없는데 왜 느릴까?
    • ‼️ 코드를 실행하는 방식 때문!

1. 한 줄씩 해석하며 실행하기 때문

  • 컴파일 언어는 미리 전체 코드를 기계어로 변환한 후 실행하지만, 인터프리터 언어는 실행할 때마다 한 줄씩 해석하고 실행
  • 컴파일 언어
    • 실행 전에 한 번만 컴파일 -> 실행 시에는 기계어(0과1)로 바로 실행 -> 속도가 빠름
  • 인터프리터 언어
    • 실행할 때마다 소스 코드를 읽고, 해석하고, 실행 -> 한 줄씩 번역하면서 실행해야 해서 속도가 느림

2. 동적 타입 (dynamic typing) 지원이 많아서

  • 대부분의 인터프리터 언어 (Python, JavaScript 등)는 동적 타입 (dynamic typing)을 지원
  • 동적 타입 언어는 실행 중 변수의 타입을 결정하기 때문에 매 실행마다 타입 체크가 필요
    • 반면, 컴파일 언어(C, C++ 등)는 컴파일 시점에 타입이 결점되므로 실행 속도가 훨씬 빠름

3. 최적화 부족

  • 컴파일러는 최적화(Optimization)를 통해 코드를 더 효율적으로 변환함
    • 예를 들어, 컴파일러는 불필요한 연산을 제거하고, 반복문을 최적화하는 등 실행 속도를 높이기 위해 다양한 작업 수행
  • 인터프리터 언어는 실행할 때마다 소스 코드를 직접 해석하기 때문에 이런 최적화가 어려움

4. 메모리 관리 방식 차이

  • 컴파일 언어는 메모리 할당을 미리 최적화할 수 있지만, 인터프리터 언어는 실행 중에 동적으로 메모리를 할당하는 경우가 많아 메모리 관리 부담이 큼

그럼 인터프리터 언어의 속도를 높이는 방법은?

✅ 1. JIT(Just-In-Time) 컴파일

  • JIT 컴파일러는 인터프리터 방식과 컴파일 방식의 장점을 결합한 방법
    • 프로그램 실행 도중 자주 실행되는 코드 블록을 미리 기계어로 변환해서 속도를 높임
  • 대표적 예시
    • Java의 JVM (HotSpot JIT 컴파일러)
    • Python의 PyPy
    • JavaScriptdml V8 엔진 (Chrome, Node.js)

✅ 2. 바이트코드(Bytecode) 사용

  • 일부 인터프리터 언어는 소스 코드를 직접 해석하는 대신, 한 번 중간 코드(바이트코드)로 변환한 후 실행하는 방식을 사용
  • 예: Python의 CPython(기본 구현체)
    • Python 소스 코드(.py) -> 바이트코드(.pyc) 변환 -> 실행
    • 덕분에 매번 소스 코드를 해석하는 부담이 줄어듦

🔹 Java는 어떻게 실행될까?

  • Java는 전통적인 컴파일 언어와 인터프리터 언어의 특징을 혼합한 하이브리드 언어

1️⃣ 소스 코드 → 바이트코드 변환 (컴파일 과정)

  • Java 코드를 작성하면, javac 컴파일러가 이를 바이트코드(Bytecode) 로 변환.
  • 바이트코드는 완전한 기계어가 아니라 JVM(Java Virtual Machine)에서 실행할 수 있는 중간 코드
  • 이 과정은 전통적인 컴파일 언어(C, C++)와 비슷하지만, 완전히 기계어로 변환되지 않고 플랫폼 독립적인 코드로 남아 있음.

2️⃣ JVM에서 바이트코드 실행 (인터프리터 + JIT 컴파일)

  • Java의 실행 과정에서는 JVM(Java Virtual Machine) 이 핵심 역할을 수행
  • JVM은 바이트코드를 한 줄씩 읽고 실행하는 인터프리터 방식을 사용하지만, 성능을 높이기 위해 JIT(Just-In-Time) 컴파일러를 활용.

🔹 JIT(Just-In-Time) 컴파일러가 하는 일

JIT 컴파일러는 인터프리터와 컴파일 방식의 장점을 결합한 기술이야.

  • 실행 중 자주 사용되는 바이트코드를 분석해서 기계어로 변환(컴파일)한 후 캐싱해 둠.
  • 이후 같은 코드가 실행될 때는 다시 해석하지 않고, 컴파일된 기계어를 바로 실행해서 성능을 높여.
  • 즉, Java는 처음에는 인터프리터처럼 실행되지만, JIT이 동작하면 컴파일된 기계어 코드가 사용되므로 속도가 빨라지는 것이야.

🔹 Java는 컴파일 언어일까? 인터프리터 언어일까?

둘 다 아님! Java는 하이브리드 방식을 사용

  • 컴파일 언어처럼: 소스 코드를 바이트코드로 변환하는 컴파일 과정이 있음.
  • 인터프리터 언어처럼: JVM이 바이트코드를 한 줄씩 해석하며 실행할 수도 있음.
  • JIT을 사용하면: 실행 중 특정 코드 블록을 기계어로 컴파일해서 성능을 높임.