🎯 공부 목표
- 자바 소스파일(.java)을 JVM으로 실행하는 과정 이해하기
학습할 것
- JVM이란 무엇인가
- 컴파일 및 실행 하는 방법
- 바이트코드란 무엇인가
- JIT 컴파일러란 무엇이며 어떻게 동작하는지
- JVM 구성 요소
- JDK와 JRE의 차이
1️⃣ JVM이란 무엇인가
🔗 참고자료
- 네이버 부스트코스(쉽게 배우는 자바1) => 링크로 이동
- https://asfirstalways.tistory.com/158
- 위키백과(자바 가상 머신)
- Naver D2-JVM Internal
# JVM 이란?
자바 가상 머신(Java Virtual Machine)은 자바 바이트코드를 실행, 해석할 수 있는 주체이다.
JVM의 역할은 자바 애플리케이션을 클래스 로더(Class Loader)를 통해 읽어 들여서 자바 기본 API를 동적으로 연결해 자바 프로그램을 실행하는 것입니다.
- 기술적 정의
-> 코드를 실행하고 해당 코드에 대해 런타임 환경을 제공하는 소프트웨어 프로그램에 대한 사양(Specification) - 일반적 정의
-> JVM은 자바 프로그램을 실행하는 방법이다. JVM의 설정을 구성한 다음 설정 사항에 따라 실행 중에 프로그램 리소스를 관리한다.
* 가상 머신 : 프로그램을 실행하기 위해 물리적 머신과 유사한 머신을 소프트웨어로 구현한 것.
#JVM의 특징
- 스택 기반의 가상 머신
-> 대표적인 컴퓨터 아키텍처인 인텔 x86 아키텍처나 ARM 아키텍처와 같은 하드웨어가 레지스터 기반으로
동작하는 데 비해 JVM은 스택 기반으로 동작한다. - 심볼릭 레퍼런스(Symbolic Reference)
-> 기본 자료형(primitive data type)을 제외한 모든 타입(클래스와 인터페이스)을 명시적인 메모리 주소 기반의
레퍼런스가 아니라 심볼릭 레퍼런스를 통해 참조한다. - 가비지 컬렉션(Garbage collection)
-> 클래스 인스턴스는 사용자 코드에 의해 명시적으로 생성되고 가비지 컬렉션에 의해 자동으로 파괴된다. - 기본 자료형을 명확하게 정의하여 플랫폼 독립성 보장
-> C/C++ 등의 전통적인 언어는 플랫폼에 따라 int 형의 크기가 변한다. JVM은 기본 자료형을 명확하게 정의하여
호환성을 유지하고 플랫폼 독립성을 보장한다. - 네트워크 바이트 오더(Network byte order)
-> 자바 클래스 파일은 네트워크 바이트 오더를 사용한다. 인텔 x86 아키텍처가 사용하는 리틀 엔디안이나,
RISC 계열 아키텍처가 주로 사용하는 빅 엔디안 사이에서 플랫폼 독립성을 유지하려면 고정된 바이트 오더를 유지해야 하므로 네트워크 전송 시에 사용하는 바이트 오더인 네트워크 바이트 오더를 사용한다.
네트워크 바이트 오더는 빅 엔디안이다. - Class 파일은 JVM이 읽을 수 있는 형태로 번역된 결과물이다.
- Class 파일은 참조하는 라이브러리를 포함하지 않고 단순히 심볼릭 레퍼런스만을 가진다.
# 자바 바이트 코드(ByteCode)
WORA(Write Once Run Anywhere) 를 구현하기 위해 JVM은 사용자 언어인 자바와 기계어 사이의 중간 언어인 자바 바이트 코드를 사용한다.
이 자바 바이트코드가 자바 코드를 배포하는 가장 작은 단위이다.
- ByteCode는 JVM을 위한 언어이다.
# 심볼릭 레퍼런스란?
심볼릭 레퍼런스는 참고하는 클래스의 특정 메모리 주소를 참조 관계로 구성한 것이 아니라 참조하는 대상의 이름만을 지칭한 것이다.
Class 파일이 JVM에 올라가게 되면 심볼릭 레퍼런스는 그 이름에 맞는 객체의 주소를 찾아서 연결하는 작업을 수행한다.
실제 메모리 주소가 아니라 이름만을 가진다.
2️⃣ 컴파일 및 실행 하는 방법
🔗 참고자료
- https://javakong.tistory.com/131
- https://zerodark.tistory.com/14
- https://www.itworld.co.kr/news/110817
# 자바로 작성된 코드 실행 전 필수사항
자바로 작성된 코드를 컴파일해서 자바 Bytecode를 생성하기 위해서는 JDK가 필수로 설치되어 있어야 합니다.
JDK(Java Development Kit)란?
자바 개발 키트로 자바 애플리케이션을 구축하기 위한 핵심 플랫폼 구성요소입니다.
이 중심에는 자바 컴파일러(Compiler)가 있습니다.
JDK는 JVM, JRE과 함께 자바 프로그래밍에 사용되는 3대 핵심 기술 패키지 가운데 하나입니다.
- JVM : 프로그램을 실행하는 자바 플랫폼 구성요소
- JRE : JVM을 생성하는 디스크 상의 부분
- JDK : 개발자들이 JVM과 JRE에 의해 실행되고 구동될 수 있는 자바 프로그램을 생성할 수 있게 해 준다.
위 사진을 보면 알 수 있듯이 JDK를 설치하면 JRE, JVM도 설치가 됩니다.
설치 및 환경변수 설정 방법
# 개발도구 없이 자바 소스코드 컴파일(Window)
openJDK를 설치했다면 해당 경로에 보면 bin 디렉터리가 있습니다.
* bin 디렉터리에는 자바로 개발하는데 필요한 주요 파일들이 들어있습니다.
- java.exe : 자바 응용 프로그램 로더.
-> javac 컴파일러가 만든 클래스 파일을 해석 및 실행한다. 현재 하나의 런처가 개발 및 배포에 동일하게 사용된다. 예전에 사용되던 배포용 런처 jre는 더 이상 Sun JDK에서는 제공되지 않고, 이 로더로 대체되었다.
* 한 줄 정리 => 자바 인터프리터, 바이트코드를 실행한다. - javac.exe : 자바 컴파일러. 자바 소스 파일을 바이트코드로 변환해준다.
- javap.exe : 클래스 파일을 자바 소스 코드로 디스어셈블해주는 도구
# 본격적으로 자바 소스코드 작성 후 컴파일해보기
class HelloWorld
{
public static void main(String[] args) {
System.out.println("Hello World!!");
}
}
- 우선적으로 해당 소스 코드를 아래와 같이 메모장에 작성합니다.
- 작성 완료 후 C 드라이브에 HelloWorld라는 폴더를 만듭니다.
* 폴더 이름을 꼭 HelloWorld로 할 필요는 없습니다. 원하시는 이름으로 만들어주세요.
- 폴더를 만들었으면 해당 폴더에 메모장 파일을 .Java 확장자로 저장합니다.
꼭 .Java 확장자로 저장을 해야 합니다.
저장을 완료했으면 cmd 창을 열어 위와 같이 입력합니다.
- cmd창 명령어인 cd(Change Directory) 명령어를 사용해 해당 경로 이동
- javac 명령어를 통해 자바 소스 파일을 자바 바이트코드로 변환합니다.
=> .class 파일 생성 - javac 컴파일러가 만든 클래스 파일을 java 명령어를 통해 해석 및 실행한다.
=> 위 사진에서 볼 수 있듯이 Hello World 가 출력된 모습을 확인할 수 있습니다.
3️⃣ 바이트코드란 무엇인가
🔗 참고자료
# 바이트 코드란?
- 특정 하드웨어가 아닌 가상 컴퓨터에서 돌아가는 실행 프로그램을 위한 이진 표현법이다.
- 하드웨어가 아닌 소프트웨어에 의해 처리되기 때문에, 보통 기계어보다 더 추상적이다.
- 특정 하드웨어에 대한 의존성을 줄이고, 인터프리팅도 쉬운 결과물을 생성하고자 하는 프로그래밍 언어에 의해,
출력 코드의 한 형태로 사용된다. - 컴파일되어 만들어진 바이트코드는 특정 하드웨어의 기계 코드를 만드는 컴파일러의 입력으로 사용되거나, 가상 컴퓨터에서 바로 실행된다.
- 사람이 읽기 쉽도록 쓰인 소스 코드와 비교하면 바이트 코드는 덜 추상적이며, 더 간결하고, 더 컴퓨터 중심적이다.
# 자바 바이트 코드란?
자바 컴파일러를 통해 자바 가상 머신이 이해할 수 있는 언어로 변환된 자바 소스 코드를 의미합니다.
자바 바이트 코드는 자바 가상 머신(JVM)만 설치되어 있으면, 어떤 운영체제에서라도 실행 될 수 있습니다.
- 자바 바이트코드는 JVM이 실행하는 명령어의 집합
- 컴파일하면 생성되는 .class 파일이 바이트 코드를 담고 있습니다.
- OS나 개발환경에 관계없이 명령어 집합을 사용 할 수 있다.
-> 자바의 크로스 플랫폼 동작 가능
# 자바 바이트 코드를 더 깊게 알려주는 사이트
https://iamsang.com/blog/2012/08/19/introduction-to-java-bytecode/
- 자바 바이트 코드를 공부하는 이유
- 자바 바이트 코드 명령어
- 소스 코드가 자바 바이트 코드로 변환된 결과
-> if와 switch 문의 차이를 자바 바이트 코드로 분석
해당 링크에 위의 내용 외에도 다양한 내용이 담겨 있습니다.
자바 바이트 코드를 더욱 깊게 알아보고 싶으신 분들은 읽어 보시는 걸 추천드립니다.
4️⃣ JIT 컴파일러란 무엇이며 어떻게 동작하는지
🔗 참고자료
# JIT 컴파일러(Just-In-Time compiler)란?
프로그램이 실행 중인 런타임에 실제 기계어로 변화해 주는 컴파일러를 의미합니다.
동적 변환(Dynamic translation)이라고도 불리는 이 기법은 프로그램의 실행 속도를 향상시키기 위해 개발되었습니다.
즉, JIT 컴파일러는 자바 컴파일러가 생성한 자바 바이트 코드를 런타임에 바로 기계어로 변환하는 데 사용합니다.
자바 바이트 코드는 기계가 바로 수행할 수 있는 언어보다는 비교적 인간이 보기 편한 형태로 기술된 것이다.
그래서 실행 엔진은 이와 같은 바이트코드를 실제로 JVM 내부에서 기계가 실행할 수 있는 형태로 변경하며, 그 방식은 다음 두 가지가 있다.
인터프리터
- 바이트코드 명령어를 하나씩 읽어서 해석하고 실행한다.
- 하나씩 해석하고 실행하기 때문에 바이트코드 하나하나의 해석은 빠른 대신,
인터프리팅 결과의 실행은 느리다는 단점을 가지고 있다. - 바이트코드라는 '언어'는 기본적으로 인터프리터 방식으로 동작한다.
JIT(Just-In-Time) 컴파일러
- 인터프리터의 단점을 보완하기 위해 도입된 것이 JIT 컴파일러이다.
- 인터프리터 방식으로 실행하다가 적절한 시점에 바이트코드 전체를 컴파일하여 네이티브 코드로 변경하고,
이후에는 해당 메서드를 더 이상 인터프리팅하지 않고 네이티브 코드로 직접 실행하는 방식이다. - 네이티브 코드를 실행하는 것이 하나씩 인터프리팅하는 것보다 빠르고,
네이티브 코드는 캐시에 보관하기 때문에 한 번 컴파일된 코드는 계속 빠르게 수행되게 된다.
4️⃣ JVM 구성요소
🔗 참고자료
- 네이버 부스트코스(쉽게 배우는 자바1) => 링크로 이동
- https://asfirstalways.tistory.com/158
- 위키백과(자바 가상 머신)
- Naver D2-
- https://www.slipp.net/wiki/pages/viewpage.action?pageId=8880250
# JVM 구조 및 구성
자바로 작성한 코드는 다음 그림과 같은 과정을 통해 수행된다.
출처: NVER D2
클래스 로더(Class Loader)가 컴파일된 자바 바이트코드를 런타임 데이터 영역(Runtime Data Areas)에 로드하고,
실행 엔진(Excution Engine)이 자바 바이트코드를 실행한다.
- Class Loader(클래스 로더)
- JVM내로 클래스(.class파일)를 로드하고, 링크를 통해 치하는 작업을 수행하는 모듈이다.
- Runtime 시에 동적으로 클래스를 로드한다.
- jar파일 내 저장된 클래스들을 JVM위에 탑재하고 사용하지 않는 클래스들은 메모리에서 삭제한다.
- (컴파일러 역할) 자바는 동적 코드, 컴파일 타임이 아니라 런타임에 참조한다.
즉, 클래스를 처음으로 참조할 때, 해당 클래스를 로드하고 링크한다는 것이다. - Execution Engine(실행 엔진)
- 클래스를 실행시키는 역할이다.
- 클래스 로더가 JVM내의 런타임 데이터 영역에 바이트 코드를 배치시키고, 이것은 실행 엔진에 의해 실행된다.
- 자바 바이트코드는 기계가 바로 수행할 수 있는 언어보다는 비교적 인간이 보기 편한 형태로 기술된 것이다.
- 실행 엔진은 이와 같은 바이트코드를 실제로 JVM내부에서 기계가 실행할 수 있는 형태로 변경한다. - Interpreter(인터프리터)
실행 엔진은 자바 바이트 코드를 명령어 단위로 읽어서 실행한다.
하지만 이 방식은 인터프리터 언어의 단점을 그대로 갖고 있다. 한 줄씩 수행하기 때문에 느리다는 것이다. - JIT(Just - In - Time)
- 인터프리터 방식의 단점을 보완하기 위해 도입된 JIT 컴파일러이다.
- 인터프리터 방식으로 실행하다가 적절한 시점에 바이트코드 전체를 컴파일하여 네이티브 코드로 변경하고,
이후에는 해당 더 이상 인터프리팅 하지 않고 네이티브 코드로 직접 실행하는 방식이다.
- 네이티브 코드는 캐시에 보관하기 때문에 한 번 컴파일된 코드는 빠르게 수행하게 된다.
- JIT컴파일러가 컴파일하는 과정은 바이트코드를 인터프리팅하는 것보다 훨씬 오래걸리므로 한번만 실행되는 코드라면 컴파일하지 않고 인터프리팅 하는 것이 유리하다.
- JIT 컴파일러를 사용하는 JVM들은 내부적으로 해당 메서드가 얼마나 자주 수행되는지 체크하고, 일정 정도를 넘을 때에만 컴파일을 수행한다.
* 네이티브 코드: 언매니지드 코드라고도 불립니다. 코드가 바로 Machine Code로 컴파일되고, 컴파일한 Machine과 동일한 칩 등을 사용하는 환경에서만 실행이 가능합니다.(예를 들면 C언어)
4️⃣ JDK와 JRE의 차이(자바 기초스터디 1 - 6)
🔗 참고자료
# JRE(Java Runtime Environment) 란?
JVM(자바 가상 머신), 자바 클래스 라이브러리(Java class library), 자바 명령(Java command) 및 기타 인프라를
포함한 컴파일된 자바 프로그램을 실행하는데 필요한 패키지이다.
- 자바 애플리케이션을 생성하고 실행하기 위한 일련의 구성 요소입니다.
- JDK(Java Development Kit)의 일부입니다.
- JVM, 자바 클래스 라이브러리, 자바 클래스 로더로 구성됩니다.
JRE의 용도
- 자바 소프트웨어 개발에 사용되고, 프로그래밍 툴 및 배포 기술을 제공합니다.
- JRE는 JDK를 사용하여 작성된 자바 코드를 JVM에서 이의 실행에 필요한 필수 라이브러리와 결합한 후
결과 프로그램을 실행하는 JVM의 인스턴스를 작성합니다. - JVM은 다수의 운영체제에 사용 가능하며, JRE를 사용하여 작성된 프로그램이 모든 운영체제에서 실행됩니다.
# JDK(Java Development Kit)란?
자바 개발 키트로 자바 애플리케이션을 구축하기 위한 핵심 플랫폼 구성요소입니다.
이 중심에는 자바 컴파일러(Compiler)가 있습니다.
JDK는 JVM, JRE과 함께 자바 프로그래밍에 사용되는 3대 핵심 기술 패키지 가운데 하나입니다.
# JDK 와 JRE의 차이점
JDK : Standard Edition, Enterprise Edition 및 Micro Edition과 같은 자바 플랫폼에서 자바 응용프로그램을 개발하는 데 사용되는 소프트웨어 개발 환경입니다.
JRE : 자바 응용 프로그램을 실행하는 데 필요한 최소 요구 사항을 제공하는 JDK의 일부입니다.
JDK와 JRE의 차이점은 JDK는 자바 프로그램을 개발하고 실행할 수 있는 환경을 제공하는 반면
JRE는 자바 프로그램을 실행하는 환경 만 제공한다는 것입니다.
요약 => JDK는 JDE와 개발 도구의 조합, JRE는 JVM과 라이브러리 파일의 조합입니다.