컴파일 언어와 인터프리트 언어
컴파일 언어와 인터프리트 언어는 프로그래밍 언어를 실행하는 방식이 다르다.
- 컴파일 언어
- '컴파일러(compiler)'를 사용하여 소스 코드를 기계어로 번역한 후 실행 파일을 만들고, 이를 실행
- 컴파일러(compiler): 고급 언어로 작성 된 소스 코드를 저급 언어로 번역하는 프로그램
- 고급 언어: 사람이 이해하기 쉽도록 작성된 프로그래밍 언어 ex) C, C++, Java, Python, ..
- 저급 언어: 컴퓨터 내부에서 바로 처리 가능한 프로그래밍 언어. ex) 기계어, 어셈블리어 등
- 대표적인 컴파일 언어: C, C++, Java
- 장점: 실행 속도가 빠르고, 실행 파일이 독립적으로 동작 가능
- 딘점: 컴파일 과정이 번거롭고, 플랫폼 의존성이 있음
- 플랫폼 의존성이란, A라는 OS에서는 동작하는데 B라는 OS에서는 동작하지 않는 것과 같은 것을 의미
- '컴파일러(compiler)'를 사용하여 소스 코드를 기계어로 번역한 후 실행 파일을 만들고, 이를 실행
- 인터프리터 언어
- '인터프리터(interpreter)'가 소스 코드를 한 줄씩 해석하며 실행 (= 컴파일 과정 없음)
- 인터프리터(interpreter): 프로그래밍 언어의 소스 코드를 바로 실행하는 컴퓨터 프로그램
- 대표적인 인터프리터 언어: Python, JavaScript
- 장점: 컴파일 과정이 없어서 결과 확인이 빠르고 유연함
- 단점: 실행 시마다 인터프리트 과정이 반복 수행되어 실행 속도가 상대적으로 느림
- '인터프리터(interpreter)'가 소스 코드를 한 줄씩 해석하며 실행 (= 컴파일 과정 없음)
그런데, Java는 컴파일 언어와 인터프리트 언어의 장점을 결합한 독특한 구조를 가진다.
Java 코드는 먼저 바이트 코드로 컴파일된 후 JVM에서 해석된다. 컴파일을 통해 실행 파일을 만들다보니 인터프리터 언어에 비해 상대적으로 실행 시간이 빠르며, JVM이 플랫폼 의존성을 제거해주어 어느 플랫폼에서나 Java 바이트 코드만 있다면 이를 실행할 수 있다.
Java 바이트 코드가 뭔지, JVM이 뭔지, 하나하나 알아보자.
JVM (Java Virtual Machine)
Java Byte Code
Java는 플랫폼 독립성을 제공하기 위해 'Java 바이트 코드'라는 중간 언어로 컴파일된다
- Java 바이트 코드는
.class
파일 형태로 저장 - 컴파일러에 의해 변환된 코드의 명령어 크기가 1바이트라서 바이트 코드라고 불림
- 가상머신(JVM, CLR 등)에서 사용되는 코드 개념으로, 가상머신이 이해할 수 있는 중간 레벨로 컴파일 한 것
- 실행되기 위해서는 컴파일러(javac 등)에 의해 한번 더 변환되어야 함 (단점)
- 일종의 바이너리 코드에 대한 추상화라고 볼 수 있음
- 각 플랫폼에서 JVM은 이 바이트 코드를 해석하여 실행 가능
- JVM이 설치되어 있다면, 어떤 운영체제에서라도 실행될 수 있음
=> 한 번 작성된 Java 프로그램은 다양한 플랫폼에서 실행 가능!
=> 이를 "Write Once, Run Anywhere"라고 함
JVM 개념
- JVM(Java Virtual Machine)은 Java 프로그램이 실행되는 가상 환경을 제공
- Java로 작성된 모든 프로그램은 JVM에서만 실행 가능
- = 자바 프로그램을 실행시키기 위해서는 반드시 JVM이 필요
- 실제 하드웨어와 운영체제와는 독립적으로 동작하며, Java 바이트 코드를 해석하고 실행하는 역할을 함
- JVM은 플랫폼별로 구현되며, Java 프로그램이 동일하게 동작하도록 보장 => 플랫폼 의존성 제거
- JVM 자체는 운영체제에 종속적이므로, 각 운영체제에 맞는 JVM을 설치해야 함
- 단점: JVM을 거쳐야 하기에 Java는 상대적으로 다른 프로그램보다 실행 속도가 느림
- Java는 .java 파일을 .class 파일인 바이트 코드로, .class 파일을 다시 바이너리 코드로, 총 2번의 컴파일 과정을 겪어야 함
- 이를 보완하기 위해 JIT 컴파일러가 도입되어 큰 성능 향상을 이루었음 (그럼에도 C언어보다는 느림..)
=> JVM은 Java 프로그램의 언어적 플랫폼 독립성을 가능하게 만드는 핵심 요소
JVM의 구성
JVM은 전반적인 구성은 위 그림과 같다. 크게 Class Loader, Runtime Data Areas, Execution Engine으로 나뉜다. 각 요소에 대해 알아보자!
JVM의 구성 - 클래스 로더(Class Loader)
- 컴파일된 바이트 코드(.class)들을 JVM의 메모리 영역(Runtime Data Area)에 배치하는 모듈
- 클래스를 메모리에 올릴 때(Loading) 한번에 올리는 정적인 방식이 아닌, 필요한 클래스만을 올리는 '동적인 방식'을 사용
- 클래스 로더의 3단계
- Loading(로드): 클래스 파일들을 가져와서 JVM의 메모리에 로드
- Bootstrap Classloader
- Extention Classloader
- System Classloader
- Linking(링크) : 클래스 파일을 사용하기 위해 검증하는 과정
- Verifying(검증) : 읽어들인 클래스가 JVM 명세에 명시된 대로 구성되어 있는지 검사
- Preparing(준비) : 클래스가 필요로 하는 메모리를 할당
- Resolving(분석) : 클래스의 상수 풀 내 모든 심볼릭 레퍼런스를 다이렉트 레퍼런스로 변경
- Initialization(초기화) : 클래스 변수들을 적절한 값으로 초기화 (static 필드들을 설정된 값으로 초기화 등)
- Loading(로드): 클래스 파일들을 가져와서 JVM의 메모리에 로드
JVM의 구성 - 런타임 데이터 영역(Runtime Data Area) = 메모리 구조
- JVM의 메모리 영역으로 자바 애플리케이션을 실행할 때 사용되는 데이터들을 적재하는 영역
- 구성
- 메소드 영역: 클래스 정보, 상수, 메소드 코드 등이 저장
- 모든 스레드가 공유하는 영역
- 힙(Heap): 객체와 인스턴스 변수가 저장되는 영역
- 모든 스레드가 공유하는 영역
- 스택(Stack): 메소드 호출 시의 지역 변수와 연산 결과를 저장
- 각 스레드의 고유한 영역
- PC 레지스터: 현재 실행 중인 명령 주소를 기록
- 각 스레드의 고유한 영역
- 네이티브 메서드 스택(Native Method Stack): 네이티브 코드를 실행하기 위한 스택
- 각 스레드의 고유한 영역
- 메소드 영역: 클래스 정보, 상수, 메소드 코드 등이 저장
JVM의 구성 - 실행 엔진(Execution Engine)
- 클래스 로더를 통해 런타임 데이터 영역에 배치된 바이트 코드를 명령어 단위로 읽어서 실행하는 모듈
- 인터프리터 방식과 JIT 컴파일러 방식을 혼합하여 바이트 코드 실행
- 구성
- 인터프리터: 바이트 코드를 한 줄씩 해석하여 실행
- 기본적으로 바이트코드는 인터프리터 방식으로 동작하지만, 전반적인 속도가 느림
- 이를 보완하기 위한 것이 JIT 컴파일러
- JIT(Just-In-Time) 컴파일러: 자주 실행되는 코드 블록을 기계어로 변환하여 실행 속도를 높임
- 가비지 컬렉터(Garbage Collector, GC): Heap 영역에서 더 이상 사용되지 않는 메모리를 자동으로 회수
- 인터프리터: 바이트 코드를 한 줄씩 해석하여 실행
가비지 컬렉터에 대해서는 다음 포스팅에서 다루겠다!
JVM의 동작 방식
- 자바 프로그램을 실행하면 JVM은 OS로부터 메모리를 할당받음
- 자바 컴파일러(javac)가 자바 소스코드(.java)를 자바 바이트 코드(.class)로 컴파일
- Class Loader가 동적 로딩을 통해 필요한 클래스들을 로딩 및 링크 하여 Runtime Data Area에 배치
- Runtime Data Area에 로딩된 바이트 코드는 Execution Engine을 통해 해석됨
- 이 과정에서 Execution Engine에 의해 Garbage Collector의 작동과 Thread 동기화가 이루어짐
JRE (Java Runtime Environment)
JRE 개념
- JRE(Java Runtime Environment)는 Java 프로그램을 실행하기 위한 런타임 환경
- Java 애플리케이션을 실행하는 데 필요한 최소한의 구성 요소를 제공
- JVM과 Java 클래스 라이브러리를 포함
- 개발 도구는 포함되지 않으며, 실행만을 위한 환경
- 기본적으로 JDK에 포함되어 있기 때문에 JDK 설치 시 함꼐 설치
- 기존에는 개별적으로 설치가 가능했지만 JDK11 버전부터는 따로 제공되지 않는다 => JDK와 JRE의 구분이 모호해짐
JRE 구성
- JVM: Java 바이트 코드를 실행하는 핵심 구성 요소
- 클래스 라이브러리(Java Class Library): Java 프로그램이 사용할 수 있는 표준 클래스의 모음
- 다양한 지원 소프트웨어 툴 및 기능: Java 프로그램 실행을 지원하는 설정 파일 및 리소스 파일 등
- java.lang 패키지, java.util 패키지
- 통합 라이브러리: Java IDL(CORBA), JDBC, JNDI 등
- 유저 인터페이스 툴킷: JavaFX, Swing, AWT 등
JDK (Java Development Kit)
JDK 개념
- JDK(Java Development Kit)는 Java 애플리케이션을 개발하기 위한 도구
- JRE뿐만 아니라 컴파일러와 디버거 같은 개발 도구가 포함되어 있음
- JDK는 개발자가 Java 애플리케이션을 작성, 컴파일, 실행, 디버깅할 수 있도록 지원
- JDK 디렉터리에서 jre 디렉터리를 확인할 수 있고, bin 폴더 내부에서 다양한 개발 도구들을 확인할 수 있다
JDK 구성
- JRE
- JVM과 Java 클래스 라이브러리를 포함
- 개발 도구
- javac: Java 컴파일러로, 소스 코드를 바이트 코드로 변환(컴파일)
- java: Java 인터프리터로, JVM을 통해 바이트 코드를 해석하고 실행
- jdb: Java 디버거
- javadoc: Java 문서(HTML 형식)를 자동으로 생성하는 도구
- jar: 자바 클래스 파일을 압축한 자바 아카이브 파일(.jar) 생성
- etc..
자바는 유료다?
자바는 Oracle에서 관리하지만 Java 소스 코드 자체는 오픈소스 이기에 수많은 JDK들이 유료 혹은 무료로 공개되어 있다.
대표적으로 Oracle의 경우, 유료 라이센스인 Oracle JDK와, 무료 라이센스인 OpenJDK가 있다. 유료 라이센스의 경우 더 많은 기능과 더 좋은 성능을 보여주는 것이 일반적이지만 무료 라이센스로도 충분히 괜찮은 성능을 보여주기 때문에 일반적인 개인의 경우 유료 라이센스를 사용하는 경우는 잘 없다.
대표적인 JDK 종류는 다음과 같다
- Oracle JDK : Oracle에서 제공하는 JDK
- Open JDK : 유명한 무료 JDK. 하지만 OpenJDK를 직접 사용하는것 보다는, OpenJDK 기반으로 빌드된 JDK 사용을 추천
- Azul Zulu : 인지도가 높은 JDK 중 하나이며, Mac 등에서 사용할 수 있는 바이너리를 제공하는 것이 특징
- Amazon Corretto : AWS에서 제공하는 JDK. AWS 환경에 최적화되어 있으며, AWS 환경이 아니더라도 사용 가능
- Temurin (AdoptOpenJDK) : Eclipse에서 제공하는 JDK. Eclipse를 사용한다면 Temurin 설치를 추천
'Java' 카테고리의 다른 글
[Java] 자바 기본 개념 정리 (기본 문법 제외) (2) | 2025.01.02 |
---|---|
[Java] Garbage Collection (3) | 2025.01.02 |
[Java] Annotation (0) | 2024.10.10 |
[Java] Reflection (0) | 2024.10.10 |
[Java] I/O (0) | 2024.10.10 |