-
Java) 컴파일, 인터프리터 그리고 자바CS 2024. 11. 10. 23:37
개요
우리가 흔히 사용하는 프로그래밍 언어로 작성된 코드는 컴퓨터에게 어떤 의미로 다가갈까요? 우리가 작성한 코드, 즉 고급 언어는 인간에게는 이해하기 쉬운 형태지만, 컴퓨터는 이를 바로 실행할 수 없습니다. 그렇다면 이 코드가 컴퓨터가 이해할 수 있는 언어로 변환되는 과정에는 어떤 방법이 있을까요? 바로 컴파일과 인터프리터라는 두 가지 주요 방식이 존재합니다. 이번 글에서는 컴파일과 인터프리터가 무엇인지, 그리고 각 방식의 특징과 차이점을 알아보겠습니다.
스크립트 언어와 컴파일 언어 차이 컴파일
컴파일은 고급 언어로 작성된 프로그램 코드를 컴파일러를 사용하여 한 번에 기계어(또는 중간 형태의 바이트코드)로 번역하는 방식입니다. 컴파일러는 소스 코드를 분석하여 최적화하고, 전체 코드를 한 번에 변환해 실행 가능한 파일(executable file)로 만듭니다.
- 과정: 소스 코드 → 컴파일러 → 기계어 코드(실행 파일)
- 언어 예시: C, C++, Go 등
- 특징:
- 빠른 실행 속도: 프로그램이 미리 컴파일된 상태로 실행되기 때문에, 실행 중에는 번역 과정이 없어 속도가 빠릅니다.
- 코드 최적화 가능: 컴파일러가 최적화를 수행하여 더 효율적인 기계어 코드를 생성할 수 있습니다.
- 의존성: 컴파일된 프로그램은 특정 운영 체제나 하드웨어 아키텍처에 의존하는 경우가 많습니다.
- 장점:
- 실행 시 속도가 매우 빠릅니다.
- 코드를 한 번에 최적화할 수 있어 성능 향상 가능성이 높습니다.
- 단점:
- 컴파일하는 과정이 시간이 걸리며, 소스 코드가 변경될 때마다 다시 컴파일해야 합니다.
- 컴파일된 바이너리는 다른 플랫폼에서 실행하기 어려울 수 있습니다.
인터프리터
인터프리터는 소스 코드를 한 줄씩 읽어서 그때그때 실행하는 방식입니다. 컴파일 과정이 필요 없으며, 코드가 작성되는 즉시 실행할 수 있습니다. 인터프리터는 프로그램 실행 중에 소스 코드를 분석하여 해석하고, 바로 실행하기 때문에 디버깅과 개발 속도가 빠른 편입니다.
- 과정: 소스 코드 → 인터프리터 → (즉시) 실행
- 언어 예시: Python, JavaScript, Ruby, PHP 등
- 특징:
- 느린 실행 속도: 실행 중에 소스 코드를 해석하고 실행하기 때문에, 컴파일된 언어보다 상대적으로 느릴 수 있습니다.
- 즉각적인 실행: 별도의 컴파일 단계 없이 소스 코드를 즉시 실행할 수 있어 개발과 디버깅이 편리합니다.
- 이식성: 인터프리터만 설치되어 있으면 다양한 환경에서 쉽게 실행할 수 있습니다.
- 장점:
- 코드 수정 후 바로 실행 가능하여 개발 속도가 빠릅니다.
- 플랫폼 독립적이므로 호환성이 높습니다.
- 단점:
- 실행 속도가 상대적으로 느립니다.
- 인터프리터가 필요한 환경에서만 실행됩니다.
JAVA 는?
위 언어 예시에 자바(Java) 가 안나온것이 이상하다고 생각할 수 있습니다. Java는 어떤언어일까요?
Java 는 컴파일과 인터프리터의 결합 방식을 사용하여 성능과 이식성을 동시에 높이는 특징을 가집니다. 이 방식을 통해 자바는 다양한 운영 체제와 하드웨어 환경에서 효율적으로 실행될 수 있습니다.
Java 는 컴파일과 인터프리터 방식을 합쳐서 사용한다. 자바의 하이브리드 방식은 소스 코드가 바이트코드(Bytecode)로 변환된 후, JVM(Java Virtual Machine)에서 인터프리트되며, 특정 단계에서는 JIT(Just-In-Time) 컴파일러가 이를 네이티브 코드로 변환하여 실행 성능을 최적화합니다.
JVM -> 기계코드 변환 과정에 JIT 컴파일러가 동작한다. 컴파일 단계와 실행 단계
자바의 프로그램이 실행되는 과정은 아래와 같이 두 단계로 구성됩니다.
- 소스 코드에서 바이트코드로 컴파일 단계
출처 : https://velog.io/@ddangle/Java-JIT-컴파일러-sfbp9dtu
- 자바로 작성된 소스 코드는 먼저 자바 컴파일러(javac)에 의해 바이트코드로 컴파일됩니다.
- 바이트코드는 특정 플랫폼에 종속되지 않는 중간 코드 형태로, .class 파일로 저장됩니다.
- 이 바이트코드는 사람이 읽기에는 어렵지만, 모든 JVM에서 실행할 수 있어 플랫폼 독립성을 유지합니다.
- JVM에서 바이트코드 실행 단계
Benjamin J.Evans,Java Optimizing(O'Reilly Media,2019),49.
- JVM은 자바 프로그램을 실행하기 위한 가상 환경을 제공하며, 바이트코드를 읽고 실행하는 인터프리터와 JIT 컴파일러가 내장되어 있습니다.
- JVM의 인터프리터는 바이트코드를 한 줄씩 해석하여 실시간으로 실행합니다. 초기 실행 시 인터프리터가 사용되며, 즉각적으로 프로그램이 실행될 수 있는 장점이 있습니다.
- JVM은 프로그램이 자주 사용되는 부분(핫스팟)을 감지하고, 이러한 코드 부분을 JIT 컴파일러에 전달하여 네이티브 코드로 컴파일합니다. 이 최적화된 네이티브 코드는 이후에 더 빠르게 실행됩니다.
JIT 컴파일러의 역할
출처 : https://catch-me-java.tistory.com/11 JVM 내의 JIT 컴파일러는 하이브리드 방식의 핵심 요소로, 반복적으로 실행되는 코드 영역(핫스팟)을 최적화하여 실행 성능을 높입니다. 이를 통해 바이트코드가 네이티브 코드로 변환되고, 메모리에 저장되어 재사용됩니다.
- 핫스팟 최적화: JIT 컴파일러는 자주 호출되는 메서드나 루프와 같은 핫스팟을 인식하고, 이를 네이티브 코드로 변환해 실행 속도를 높입니다.
- 코드 캐싱: 변환된 네이티브 코드는 JVM이 캐싱하여, 이후 동일한 코드가 실행될 때 인터프리팅 없이 즉시 실행할 수 있습니다.
- 실행 중 최적화: JIT 컴파일러는 실행 중인 프로그램을 분석하여, 상황에 맞게 최적화를 진행합니다. 이로 인해 프로그램이 실행될수록 성능이 개선되는 효과가 있습니다.
출처 : https://catch-me-java.tistory.com/11 JVM의 컴파일 단위가 메서드이기 때문에 메서드가 잘 분리되어 있으면 JVM은 컴파일 우선순위를 효율적으로 결정하고, 인라이닝 최적화 등의 기법을 적용하기 용이합니다.
하이브리드 방식의 장점
- 이식성: 바이트코드로 컴파일된 자바 프로그램은 JVM이 설치된 다양한 플랫폼에서 실행할 수 있어 이식성이 높습니다.
- 성능 최적화: 인터프리터를 통한 초기 실행 이후, JIT 컴파일러가 네이티브 코드로 최적화하여 실행 속도를 크게 향상시킵니다.
- 메모리 효율성: JVM이 필요하지 않은 코드 부분은 인터프리팅하고, 자주 실행되는 부분만 JIT 컴파일러가 네이티브 코드로 변환하여 메모리 효율성을 유지합니다.
- 동적 최적화: 프로그램이 실행되면서 분석을 통해 최적화가 이루어지므로, 정적인 컴파일 방식보다 유연하게 성능을 향상시킬 수 있습니다.
결론
자바의 하이브리드 방식은 컴파일과 인터프리터의 장점을 모두 활용하여, 다양한 환경에서 이식성과 성능을 동시에 유지할 수 있도록 설계되었습니다. 컴파일된 바이트코드는 JVM을 통해 인터프리팅되고, JIT 컴파일러의 동적 최적화를 통해 실행 성능을 극대화하는 방식입니다. 이를 통해 자바는 플랫폼 독립적인 언어로서 강력한 성능을 제공할 수 있습니다.
추가
네이티브 코드 (Native Code)
네이티브 코드는 컴퓨터의 CPU가 직접 실행할 수 있는 기계어 코드입니다. 이는 특정 운영 체제와 하드웨어에 맞춰 생성되며, 실행 파일로 변환되어 운영 체제에서 바로 실행할 수 있습니다.
특징 네이티브 코드 바이트 코드 실행 주체 운영 체제와 CPU가 직접 실행 가상 머신(JVM 등)에서 실행 속도 매우 빠름 상대적으로 느림, JIT 최적화를 통해 개선 가능 이식성 특정 플랫폼 종속적 플랫폼 독립적 (가상 머신이 필요) 컴파일 프로그램마다 고유의 기계어로 한 번에 컴파일 바이트코드로 컴파일 후 가상 머신에서 해석/실행 사용 예 C, C++ 등의 로우레벨 시스템 프로그램 자바, C#, 파이썬 등 가상 머신이 지원하는 언어 핫스왑(HotSwap)
자바는 기본적으로 인터프리터와 JIT 컴파일러를 결합한 하이브리드 방식으로 실행되기 때문에, 실시간으로 코드 수정이 가능할 것 같지만, 일반적인 자바 애플리케이션에서는 실시간 코드 수정이 제한적입니다. 그러나 특정 도구나 환경을 통해 제한적으로 실시간 코드 수정(핫스왑, HotSwap)이 가능합니다.
핫스왑을 통해 디버깅 중 메서드 내부 코드 수정 정도는 실시간 반영이 가능하지만, 클래스 구조 변경은 어렵습니다.
클래스 구조 변경을 실시간으로 반영하는 것이 어려운 이유는, 클래스 로딩 메커니즘, 메모리 일관성 문제, JIT 최적화 무효화, 안정성 및 보안 등의 이유로 인해 기존 구조를 수정하고 실시간 반영하기 어렵기 때문입니다.
특히 JVM은 클래스를 로드할 때 클래스 로더(ClassLoader)를 통해 메모리에 한 번 로드하며, 동일한 이름의 클래스를 다시 로드하는 것을 허용하지 않습니다.
'CS' 카테고리의 다른 글
Java) StringBuilder 와 StringBuffer (0) 2024.08.07 Java) String 부수기 (0) 2024.08.06 Java) 정규표현식 (0) 2024.08.05 Java) 가비지 컬렉션 (0) 2024.08.04