자바 라이브 스터디 #1 JVM은 무엇이며 자바 코드는 어떻게 실행하는가
목표
자바 소스 파일(.java)을 JVM으로 실행하는 과정에 대해 이해하기
학습할 것
-
JVM이란 무엇인가
-
컴파일 하는 방법
-
실행하는 방법
-
바이트코드란 무엇인가
-
JIT 컴파일러란 무엇이며 어떻게 동작하는가
-
JVM 구성 요소
-
JDK와 JRE의 차이
1. JVM이란 무엇인가
JVM : 자바 가상 기계(JVM: Java Virtual Machine)
자바 프로그램은 완전한 기계어가 아닌 중간 단계의 바이트 코드이기 때문에 이를 해석하고 실행할 수 있는 가상 운영체제가 필요하다.
이것이 바로 JVM이며, JVM은 실 운영체제를 대신해 자바 프로그램을 실행시키는 가상 운영체제 역할을 한다.
운영체제별로 프로그램을 실행하고 관리하는 방법이 다르기 때문에 운영체제별로 자바 프로그램을 별도로 개발하는 것 보다는
운영체제와 자바 프로그램을 중계하는 JVM을 두어 자바 프로그램이 여러 운영체제에서 동일한 실행결과가 나오도록 설계한 것이다.
바이트 코드는 모든 JVM에서 동일한 실행 결과를 보장하지만, JVM은 운영체제에 종속적이기 때문에 자바 프로그램을 운영체제가
이해하는 기계어로 번역해서 실행하기 위해서는 JVM은 운영체제에 맞게 설치되어야 한다.
2. 컴파일 하는 방법
자바 프로그램을 개발할 때
① .java 소스 파일 작성 ② 컴파일러(javac.exe)로 바이트 코드 파일(.class) 생성 ③ JVM 구동 명령어(java.exe) 순으로 진행된다.
자바 프로그램을 개발하기 위해 먼저 파일 확장자명이 .java인 텍스트 파일을 생성하고 소스를 작성한다.
이렇게 만들어진 파일을 자바 소스 파일이라고 하며, 작성 완료된 자바 소스 파일은 컴파일러(javac.exe)로 컴파일해야 한다.
예를 들어 Hello.java 자바 소스 파일을 생성하고 컴파일 한다면 명령 프롬프트에서 javac Hello.java 입력하여 컴파일 하면
Hello.class 파일이 생성되고 이는 바이트 코드 파일(.class)이다.
IDE를 사용하는 경우, 자바 소스 파일을 저장함과 동시에 컴파일이 이루어진다고 볼 수 있다.
3. 실행하는 방법
컴파일 결과로 생성된 바이트 코드 파일은 완전한 기계어가 아니기 때문에 단독으로 실행할 수 없고 JVM이 실행되어야 한다.
JVM을 구동시키는 명령어는 java.exe이다.
예를 들어 Hello.class라는 바이트 코드 파일을 java.exe로 실행하려면 명령 프롬프트에 java Hello를 입력하면 된다.
주의할 점은 .class 확장자명을 제외한 이름을 입력해야 한다.
4. 바이트코드란 무엇인가
특정 하드웨어가 아닌 가상 컴퓨터에서 돌아가는 실행 프로그램을 위한 이진 표현법이다.
하드웨어가 아닌 소프트웨어에 의해 처리되기 때문에 보통 기계어보다 더 추상적이다.
5. JIT 컴파일러란 무엇이며 어떻게 동작하는가
JIT 컴파일이란 Just-In-Time Compilation의 약어로 프로그램을 실제 실행하는 시점에 기계어로 번역하는 컴파일 방법이다.
JVM에서는 JIT 컴파일을 지원하고 있어 자바 컴파일러가 자바 프로그램을 바이트 코드로 변환한 다음에 실제 바이트 코드를
실행하는 시점에서 JVM이 바이트 코드를 JIT 컴파일러를 통해 기계어로 변환한다.
JIT는 인터프리트 방식과 정적 컴파일 방식을 혼합한 방식으로 생각할 수 있다. 즉, 실행 시점에 인터프리트 방식으로 기계어 코드를 생성하면서 그 코드를 캐싱하여 같은 함수가 여러 번 불릴 때마다 매번 기계어 코드가 생성되는 것을 방지한다.
6. JVM 구성 요소
Class Loader
자바 소스 파일(.java)을 컴파일 하게되면 바이트 코드 파일(.class)이 생기게 되고 JVM Class Loader에서 이를 관리하게 된다.
Class Loader은 ① loading ② linking ③ initialization 3가지 동작을 수행한다.
① loading
클래스들을 로드하기 위해 JVM은 세 가지 유형의 클래스 로더들을 가지고 있으며, Bootstrap, extension, application Class Loader이다.
먼저 Bootstrap Class Loader가 클래스를 찾기 위해 JRE lib 폴더 안에 있는 rt.jar 파일을 스캔한다.
클래스가 발견되지 않으면 extension Class Loader가 jre\lib\ext 폴더 안에 있는 클래스 파일을 탐색한다.
그래도 클래스가 발견되지 않는다면 application Class Loader가 CLASSPATH 안에 있는 모든 Jar 파일과 클래스를 탐색한다.
만약 어느 Loader를 통해 클래스가 발견된다면 클래스는 Class Loader에 의해 로드되고, 발견되지 않는다면ClassNotFoundException이 발생한다.
② linking
클래스가 로더된 후에는 linking이 수행되며, bytecode verifier에 의해 바이트 코드가 적절하게 만들어졌는지 검증한다.
만약 검증 단계에서 적절치 않은 바이트 코드를 발견하는 경우 verification error가 발생한다.
linking 과정에서는 검증 과정을 통해 클래스 안에 있는 static 변수와 static 함수에 메모리 할당 또한 수행한다.
③ initialization
class loading의 마지막 수행 단계로 모든 static 변수들은 값을 할당받고 static 블럭이 실행된다.
JVM Memory
JVM 안에 존재하는 메모리 부분은 Method Area, Heap, Stacks, PC register, Native Method Stacks로 나눌 수 있다.
- Method Area
: 모든 클래스 수준(부모 클래스, 자식 클래스, 함수, 변수)의 데이터가 저장되며, 공유 자원으로 JVM당 하나의 영역만 존재한다.
- Heap
: 어플리케이션이 실행되는 동안 모든 오브젝트가 저장되며, 공유 자원으로 JVM당 하나의 영역만 존재한다.
- Stacks
: 각 스레드는 하나의 JVM Stack을 가지고 있으며, 모든 지역 변수가 스택 메모리에 저장된다.
- PC register
: 현재 실행 중인 명령문의 물리적인 주소를 저장하며, 각 스레드마다 PC register를 개별적으로 가지고 있다.
- Native Method Stacks
: Native Method를 가지고 있는 스택으로 개별 스레드마다 생성된다.
Execution Engine
JVM 내에 있는 모든 코드들은 실행 엔진에 의해 실행되며, 실행 엔진은 바이트 코드를 읽고 하나씩 실행한다.
실행 엔진 내에는 바이트 코드를 기계어로 변환하고 실행하기 위해 인터프리터와 JIT 컴파일러가 내장되어 있다.
Native Method Interface
실행 엔진에 필요한 원시 라이브러리를 제공하며 Java와 다른 언어를 연동할 수 있도록 돕는 솔루션이다.
Native Method Libraries
Native Libraries의 집합이며 실행 엔진에 필수적이다.
7. JDK와 JRE의 차이
JDK는 프로그램 개발에 필요한 자바 가상 기계(JVM), 라이브러리 API, 컴파일러 등의 개발 도구가 포함되어 있다.
JRE에는 프로그램 실행에 필요한 자바 가상 기계(JVM), 라이브러리 API만 포함되어 있다.
즉 자바 프로그램을 개발하고자 한다면 컴파일러가 포함된 JDK를 설치해야 하며, 이미 개발된 자바 프로그램을 실행하고자 한다면 JRE만 설치하면 된다.