자바 온라인 스터디 - 1주차: "JVM은 무엇이며 자바 코드는 어떻게 실행하는 것인가"
https://github.com/whiteship/live-study
백기선님 자바 온라인 스터디 1주차!
https://github.com/whiteship/live-study/issues/1
🎯 목표: 자바 소스 파일(.java)을 JVM으로 실행하는 과정 이해하기.
✏️ 학습할 것
- JVM이란 무엇인가
- 컴파일 하는 방법
- 실행하는 방법
- 바이트코드란 무엇인가
- JIT 컴파일러란 무엇이며 어떻게 동작하는지
- JVM 구성 요소
- JDK와 JRE의 차이
1. JVM이란 무엇인가
JVM(Java Virtual Machine)이란, '자바를 실행하기 위한 가상머신'이다.
자바 애플리케이션은 JVM을 통해 OS(운영체제)와 독립적이다.
일반 애플리케이션의 경우, 다른 OS에서 실행시키기 위해서는 애플리케이션을 해당 OS에 맞게 변경해야 하지만, 자바 애플리케이션은 JVM과 상호작용을 하기 때문에 다른 OS에서도 프로그램 수정 없이 실행이 가능하다.
Java Compiler는 .java
파일을 .class
라는 Java byte code로 변환시켜준다. Java Byte Code는 기계어가 아니기 때문에 OS에서 바로 실행되지 않는다. 이 때, JVM이 OS가 Byte code를 이해할 수 있도록 해석해준다.
애플리케이션 => Java Compiler => JVM => OS 로 JVM의 해석 과정이 존재하기 때문에 실행 속도가 느리다는 단점이 있지만, Byte code를 하드웨어의 기계어로 바로 변환해주는 JIT 컴파일러와 향상된 최적화 기술을 통해 네이티브 언어와의 속도 격차가 많이 줄어들었다.
2. 컴파일 하는 방법
위에서 언급한 과정처럼 Java Compiler가 자바 소스코드를 JVM이 이해할 수 있는 Java Byte Code로 변환하는 과정을 컴파일이라고 한다. (.java
=> .class
빌드)
자바 컴파일러로 컴파일은 JDK에 포함 되어있는 javac.exe
를 실행하여 컴파일한다.
public class Main{
public static void main(String[] args){
System.out.println("Hello world!");
}
}
=> Main.java로 저장
C:\work>javac Main.java
=> 터미널에서 Main.java를 컴파일
.class
는 클래스 로더에 의해 JVM 내로 로드 되고, 실행 엔진에 의해 기계어로 해석되어 메모리 상(Runtime Data Area)에 배치되게 된다.
실행 엔진에는 Interpreter
와 JIT(Just-In-Time) Compiler
가 있다. Interperter는 바이트 코드를 한줄 씩 읽기 때문에 실행이 느린 단점이 있다.
이러한 단점을 보완하기 위해 나온 것이 JIT Compiler이다. 인터프리터 방식으로 실행하다가 적절한 시점에 바이트 코드 전체를 컴파일하고 더이상 인터프리팅 하지 않고 해당 코드를 직접 실행하는 것이다. JIT Compiler에 의해 해석된 코드는 캐시에 보관하기 때문에 한 번 컴파일 된 후에는 빠르게 수행하는 장점이 있다.
3. 실행하는 방법
실행방법 ex)
C:\work>java Main
=> Main.class파일 실행(java.exe: 자바 인터프리터) => Hello world! 출력
java <package>.<classname>
-cp
옵션으로 classpath를 지정해줄 수 있고, classname 뒤에 문자열 형태의 매개변수를 전달해줄 수 있다.
javac는 파일을 가리켜서 컴파일, java는 package에 존재하는 클래스를 가리켜서 실행
이 과정에 JVM에서 일어나는 일은 다음과 같다.
- 사용자 입력
- 해당 클래스의 main 메서드를 호출
- 클래스 로더가 해당 클래스를 로드
- 클래스는 main 메서드가 호출 되기 전에 초기화
- main 메서드가 동작하기 위해 필요한 클래스 및 인터페이스를 로드
4. 바이트코드란 무엇인가
바이트 코드(Byte Code, portable code, p-code)는 자바 컴파일러가 자바 소스 코드를 컴파일한 기계어
바이트 코드는 명령어 크기가 1byte라서 바이트 코드라고 불리며, 특정 하드웨어가 아닌 가상 머신에서 실행 되는 실행 프로그램을 위한 이진 표현법이다.
바이트 코드를 확인하기 위해서는 JDK에 포함되어 있는 javap를 사용하면 확인 가능하다.
C:\work>javap -c Main.class
https://docs.oracle.com/javase/8/docs/technotes/tools/windows/javap.html
5. JIT 컴파일러란 무엇이며 어떻게 동작하는가
ref) https://www.geeksforgeeks.org/just-in-time-compiler/
JIT 컴파일(Just-In-Time)이란 프로그램을 실제 실행하는 시점에 기계어로 번역하는 컴파일러
JIT 컴파일러는 자바 런타임(JRE)의 구성요소로, 런타임에 바이트 코드를 기계 코드로 컴파일하여 바이트 코드를 더 빠르게 실행하도록 도와준다.
인터프리트 vs 정적 컴파일
컴퓨터 프로그램 방식에는 인터프리트 방식과 정적 컴파일 방식으로 나눌 수 있다.
인터프리트 방식은 실행 중 프로그래밍 언어를 읽으며 해당 기능에 대응하는 기계어 코드를 >실행하지만 정적 컴파일 방식은 실행 전에 프로그램 코드를 기계어로 번역한다.
JIT 컴파일러는 두 가지의 방식을 혼합한 방식으로, 가장 자주 실행되는 코드 블록, 메서드 또는 메서드의 일부, 특히 반복문을 모니터링해서 그 코드를 캐싱하여 네이티브 코드로 컴파일 시킨다.
JIT 컴파일 과정은 별도의 스레드에서 실행되고, JVM 스레드는 JIT 컴파일 스레드의 영향을 받지 않기 때문에 애플리케이션의 실행에 영향을 주지 않는다. 컴파일이 진행중일 때에는 인터프리트 방식으로 동작하지만, 컴파일이 완료되면 컴파일 된 버전을 사용하게 된다. 이를 on-stack replacement(OSR) 이라고 한다.
JIT 컴파일러는 C1 (클라이언트 컴파일러), C2 (서버 컴파일러)로 구분된다. C1과 C2는 동작이 다르다. C1은 빠르게 실행되지만 덜 최적화 된 코드를 생성하며, C2는 실행 시간이 좀 더 걸리지만 더 최적화된 코드를 생성한다.
오늘날의 JVM은 두 가지를 모두 사용하며 처음엔 C1을 사용하지만, 호출의 수가 증가하게 되면 C2를 사용한다. 이를 tiered compilation 이라고 한다.
6. JVM 구성 요소
- 클래스 로더(Class Loader)
- 런타임 데이터 영역(Runtime Data Area)
- 실행 엔진(Execution Engine)
- JNI(Java Native Interface)
- Native Method Library
1) 클래스 로더 (Class Loader)
클래스 로더는 클래스 파일을 읽어들이는 부분이다. 자바는 동적 로딩을 사용하기 때문에 실제 애플리케이션에서 필요한 부분만 로딩해서 사용한다.
로딩
- BootStrap ClassLoader : 최상위 클래스 로더,
jre/lib/rt.jar
에 담긴 JDK 클래스 파일을 로딩한다. Native C로 구현되어 있다고 함. java9 이후 rt.jar가 제거되면서 범위가 축소됨 - Extension ClassLoader :
jre/lib/ext
또는java.ext.dirs
환경 변수로 지정된 폴더에 있는 클래스 파일을 로딩한다.
java9 이후Platform ClassLoader
로 명칭이 변경되었으며, JCP에 표준화 된 모듈 내의 클래스들을 볼 수 있다고 한다. - Application ClassLoader : classpath나 JAR 파일 안에 있는 Manifest파일의
Class-path
속성 값으로 지정된 폴더에 있는 클래스를 로딩한다. 개발자가 어플리케이션 구동을 위해 직접 작성한 대부분의 클래스가 이 클래스 로더에 의해 로딩된다.
링킹
- Verify: 바이트코드 검증기가 생성된 바이트 코드가 적절한지 아닌지 검증하고, 만약 검증에 실패하면 검증 실패 오류가 발생한다.
- Prepare: 모든 static 변수들의 메모리가 할당되고, 기본 값으로 정의
- resolve: 모든 심볼릭 메모리 참조가 메서드 영역의 원래 참조로 대체
초기화
Class Loader의 마지막 단계, 모든 Static 변수들은 기본 값으로 할당되고, static block이 실행된다.
2) 런타임 데이터 영역
- 메소드 영역: 클래스 레벨의 정보가 저장됨, 스레드간 공유
- 힙 영역: 모든 객체와 배열이 저장됨, GC(Garbage Collection), 스레드간 공유
- 스택 영역: 스레드 마다 생성되는 영역, 메서드에 관련된 정보가 스택 프레임의 형태로 저장됨
- PC Register: 스레드 마다 PC Register를 가진다. Java 메서드에 대한 위치만 기록
- Native Method Stack: native로 작성된 메서드의 정보를 저장하는 곳으로 스레드 마다 생성됨
3) 실행 엔진
실행 엔진은 런타임 데이터 영역에 할당된 java 바이트 코드를 읽어서 기계어로 번역해 실행하는 영역이다.
1. 인터프리터: JVM은 자바 바이트 코드를 한 라인씩 읽어서 실행
2. JIT 컴파일러: 인터프리터 방식의 단점을 보완
3. 가비지 컬렉터: 힙 영역에서 사용하지 않는 객체들을 관리
7. JDK와 JRE의 차이
자바의 세가지 Edition
- Java SE(Standard Edition): java vm과 표준 api 등을 정리한 가장 표준적인 버전이다. PC나 서버등에서 동작하는 애플리케이션을 개발 및 실행하는 경우에 사용한다.
- Java EE(Enterprise Edition): 웹 서비스나 서버 간 통신, 메일 송신 등 서버 애플리케이션에 필요한 기능이 많이 포함되어있다.
- Java ME(Micro Edition): 가전제품과 휴대전화 등의 임베디드 시스템용의 애플리케이션을 개발하기 위한 에디션, Java SE에 비해 사용할 수 있는 기능이 한정되어있지만, 필요한 리소스 (CPU나 메모리 등)가 적게 들어 임베디드 시스템에서도 잘 동작한다. 또한 자바는 실행 환경(JRE) 및 개발 환경(JDK)의 두 가지로 나누어져 있다.
JRE란, Java Runtime Environment로, 자바 소프트웨어를 실행하기 위한 소프트웨어이다. 클래스 라이브러리, 로더 클래스, JVM을 포함한다.
JDK란, Java Development Kit으로, JRE에서 제공하는 실행 환경과 자바 개발에 필요한 여러가지 명령어와 컴파일러를 포함
참조
https://www.artima.com/insidejvm/ed2/jvm2.html
https://dzone.com/articles/jvm-architecture-explained
https://www.notion.so/1-JVM-102a937ee907483cae0e673bec5489ff
https://redbean88.github.io/study/whiteship-study-1week/
https://toolsofmethuselah.tistory.com/35
https://velog.io/@minheum/%EC%9E%90%EB%B0%94-%EC%8A%A4%ED%84%B0%EB%94%94-1%EC%A3%BC%EC%B0%A8-JVM
Author And Source
이 문제에 관하여(자바 온라인 스터디 - 1주차: "JVM은 무엇이며 자바 코드는 어떻게 실행하는 것인가"), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@minjiy55/자바-온라인-스터디-1주차-JVM은-무엇이며-자바-코드는-어떻게-실행하는-것인가저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)