자바 소스 파일(.java)을 JVM으로 실행하는 과정 이해하기.
1. JVM이란 무엇인가
2. 컴파일 하는 방법
3. 실행하는 방법
4. 바이트코드란 무엇인가
5. JIT 컴파일러란 무엇이며 어떻게 동작하는지
6. JVM 구성요소
7. JDK와 JRE의 차이
Java Virtual Machine. 자바를 실행하기 위한 자바 가상머신
.java 파일을 컴파일러를 통해 .class 파일(ByteCode)로 만들고, 이를 운영체제가 이해할 수 있는 기계어로 바꿔주는 역할을 합니다.
이러한 JVM은 운영체제에 영향을 받지 않고 실행이 가능하도록 해줍니다.

Java Virtual Machine (JVM) 구조(위키 백과)
준비물 : java.exe , javac.exc
javac.exe는 자바 소스코드를 컴파일 할때 사용하는 프로그램 입니다. 이때 만들어진 .class(바이트코드 파일) 을 실행하는게 java.exe 입니다.
$ javac 소스파일명.java
위와 같이 명령어를 입력하여, .class 파일을 생성할 수 있습니다.
만들어진 .class 파일은 같은경로에 생성되며, 이름 또한 소스파일명.java의 소스파일 명을 따라갑니다.
컴파일러(javac.exe)와 실행파일(java.exe)의 JDK/JRE 버전은 일치해야 합니다. 컴파일 옵션을 통해 해결하는 방법도 있긴합니다.
$ java 소스파일명
위와 같은 명령어를 통해 java.exe 를통해 바이트 코드로 컴파일된 .class를 실행 할 수 있습니다.
프로그램을 실행하는것은 컴퓨터이기 때문에, 컴퓨터가 이해할 수 있는 기계어를 전달해주어야 합니다.
1) 바이너리 코드란?
하지만, .obj 파일은 완벽한 기계어는 아닙니다. 이 파일을 완벽한 기계어로 변환하려면 "링커"가 필요한데, 이 링커는 여러 개의 코드와 데이터를 모아 연결하여 메모리에서 실행 가능한 파일로 만드는 역할을 해줍니다.
※ 즉, 기계어는 컴퓨터가 이해할 수 있는 0과 1로 이루어져있는 바이너리 코드이지만, 기계어가 바이너리 코드로 이루어졌을 뿐이지, 모든 바이너리코드가 기계어인것은 아닙니다!
2) 바이트 코드
c언어와 다르게 Java에서는 컴파일러(javac)에 의해 소스파일(.java)이 바이트코드(.class)가 되는데, 컴퓨터는 이 바이트 코드를 바로 인식할 수는 없습니다.
JVM이 설치되어있다면, 그게 어떤 플랫폼이라도 실행을 할 수 있게 되기 때문입니다. 즉, 이 JVM을 통해 운영체제의 종류나, 개발이 수행되는 환경의 종류에 구애받지 않고, 프로그램을 실행할 수 있게 됩니다.
C나 C++처럼 프로그램을 실행하기 전에 처음 한 번 컴파일 하는 대신, 프로그램을 실행하는 시점에서 필요한 부분을 즉석으로 컴파일 하는 방식을 말합니다. (Just-In-Time)
JIT 컴파일러는 바이트코드를 읽어 빠른 속도로 기계어를 생성할 수 있습니다. 이런 기계어 변환은 코드가 실행되는 과정에 실시간으로 일어나며(Just-In-Time), 전체 코드의 필요한 부분만 변환합니다. 기계어로 변환된 코드는 캐시에 저장되기 때문에 재사용시 컴파일을 다시 할 필요는 없습니다.

Java Virtual Machine (JVM) 구조(위키 백과)
클래스 로더는 자바 바이트코드(.class)를 읽고 JVM 내로 클래스를 로드하는 역할을 합니다. 클래스 파일을 찾고, 읽고, 검사한 후, JVM의 메모리 영역에 적재합니다. 주된 작업은 다음과 같습니다.
- 로딩(Loading): 클래스 파일을 찾아 읽어들입니다.
- 링킹(Linking): 클래스 파일의 유효성을 검증하고, 참조를 해결하며, 메모리를 할당합니다.
- 초기화(Initialization): 클래스 변수들을 초기화하고 static 블록을 실행합니다.
JVM 메모리는 프로그램 실행 중에 필요한 데이터를 저장하는 여러 메모리 영역으로 나뉩니다.
- Method Area(메소드 영역): 클래스 메타데이터, 상수 풀, 스태틱 변수, 메소드 코드 등이 저장됩니다. 일반적으로 퍼머넌트 제너레이션(PermGen) 또는 메타스페이스(Metaspace)로 구현됩니다.
- Heap(힙): 모든 객체 인스턴스와 배열이 저장되는 영역입니다. 가비지 컬렉터가 이 영역을 관리하며, Young Generation과 Old Generation으로 나뉩니다.
- JVM Language Stacks(JVM 언어 스택): 각 스레드마다 존재하며, 메소드 호출 시 생성되는 프레임을 저장합니다. 프레임에는 지역 변수, 피연산자 스택, 메소드 호출/복귀 정보가 포함됩니다.
- PC Resgisters(PC 레지스터): 각 스레드마다 존재하며, 현재 실행 중인 JVM 명령의 주소를 저장합니다.
- Native Method Stacks(네이티브 메소드 스택): 네이티브 메소드(자바 이외의 언어로 작성된 메소드) 호출시 사용되는 스택입니다.
실행 엔진은 바이트코드를 실제로 실행하는 역할을 합니다. 실행 엔진은 다음의 세 가지 주요 구성 요소로 이루어져 있습니다.
- Interpreter : 바이트 코드를 한 줄씩 읽고 실행합니다. 처음에는 빠르게 실행할 수 있지만, 반복문 등에서 속도가 느려질 수 있습니다.
- JIT Compiler : 인터프리터의 단점을 보완하기 위해, 자주 사용되는 코드(핫스팟)를 기계어로 변환하여 실행 속도를 높입니다. 컴파일된 코드는 캐시에 저장되어 이후 빠르게 실행됩니다.
- Garbage Collector: 힙 영역을 관리하며, 사용되지 않는 객체를 자동으로 메모리에서 해제하여 메모리 누수를 방지합니다.
✨ Garbage Collector의 역할
네이티브 메소드 인터페이스는 자바 코드에서 네이티브(비자바) 코드를 호출할 수 있도록 해주는 프레임워크입니다. 주로 C, C++로 작성된 네이티브 라이브러리와 상호작용할 때 사용됩니다.
네이티브 메소드 라이브러리는 JVM이 네이티브 메소드 인터페이스를 통해 호출할 수 있는 네이티브 코드로 작성된 라이브러리입니다. 예를 들어, 자바에서 시스템 레벨의 기능을 사용하기 위해 운영체제 API를 호출할 때 사용됩니다.
JDK는 자바 개발도구(Java Development Kit)의 약자입니다. Java로 애플리케이션을 개발하고 컴파일할 수 있도록 해주는 개발 환경의 세트를 의미합니다. JDK는 다음과 같은 구성 요소를 포함합니다:
JDK는 JRE(Java Runtime Environment)를 포함하며, 개발에 필요한 도구들을 추가적으로 제공합니다. 따라서, 자바 애플리케이션을 개발하고 실행할 수 있는 완전한 환경을 제공하는 것이 JDK입니다.
JRE는 자바 실행환경(Java Runtime Environment)의 약자입니다. 자바 애플리케이션을 실행할 수 있는 환경을 제공합니다. JRE는 다음과 같은 구성 요소를 포함합니다:
JRE는 자바 애플리케이션을 개발할 필요는 없지만, 자바 애플리케이션을 실행할 수 있는 환경을 제공합니다. 따라서, 자바 애플리케이션을 실행하려는 사용자나 서버 환경에서는 JRE만 설치하면 충분합니다.
아주 유익한 글이네요. 붐업