1주차 과제: JVM은 무엇이며 자바 코드는 어떻게 실행하는 것인가
📜 시작에 앞서
- 백기선 님의 라이브 스터디(2020년 11월부터 2021년 3월까지) 커리큘럼을 따라 진행한 학습입니다
- 뒤늦게 알게 되어 스터디 참여는 못했지만 남아있는 스터디 깃허브 주소와 유튜브 영상을 참고했습니다
📌 목표
자바 소스 파일(.java)을 JVM으로 실행하는 과정 이해하기.
📌 학습할 것
- JVM이란 무엇인가
- 컴파일 하는 방법
- 실행하는 방법
- 바이트코드란 무엇인가
- JIT 컴파일러란 무엇이며 어떻게 동작하는지
- JVM 구성 요소
- JDK와 JRE의 차이
📑 JVM이란 무엇인가
Write Once, Run Anywhere
자바는 어떻게 한 번 작성한 코드로 여러 플랫폼에서 작동할까?
- 자바 소스 코드를 컴파일 하면 자바 바이트코드로 이루어진 .class 파일을 생성한다
- .class 파일을 각기 다른 OS에서 실행하면 각 OS에 맞는 JVM을 이용해 실행한다
- JVM은 OS 종속적이다
- 오라클에서 JVM 스팩을 정의하면 JVM 밴더가 구현. 각 밴더가 구현한 JVM마다 스팩을 따르는 안에서 차이 존재
- JVM 밴더로 아마존, 오라클, AZUL 등이 있다
- JVM에 소스 코드를 자바 바이트코드로 컴파일해 전달할 수 있다면 그 언어는 플랫폼에 독립적이다
- 자바 바이트코드로 컴파일하는 언어로 Java, Kotlin, Scala 등이 있다
📑 컴파일 하는 방법
명령어로 컴파일 하기
- 사용할 Java 버전 설치
- 자바 컴파일러(javac.exe)를 통해 소스 파일을 컴파일한다. javac <options> <source files>
- .class 파일 생성
- IDE 환경에서는 잘 되던 게, 문법상 오류가 없는 소스파일도 컴파일 되지 않거나 컴파일된 파일 실행시 에러가 뜰 수 있다
- 컴파일 옵션을 확인하자
주요 컴파일 옵션
-cp <path>
-d <directory>
-encoding <encoding>
-source <release>
-target <release>
- 지정된 버전의 VM을 대상으로 하는 클래스 파일 생성
- 이외 컴파일 옵션은
javac
로 확인
📑 실행하는 방법
- 메인 클래스 파일 실행
java [options] <mainclass> [args...]
- jar 파일 실행 옵션
java [options] -jar <jarfile> [args...]
- 메인 클래스 실행에 필요한 모듈 지정
java [options] -m <module>[/<mainclass>] [args...] java [options] --module <module>[/<mainclass>] [args...]
- 단일 소스 파일 실행
java [options] <sourcefile> [args]
- 이외 실행 옵션
java
로 확인
자바 상위 버전에서 컴파일한 파일을 하위 버전에서 돌릴 수 있을까?
- 기본적으로 하위 버전의 바이트코드는 상위 버전에서 실행 할 수 있다
- 상위 버전의 바이트코드를 하위 버전에서 실행하려면
-source<release>
, -target <release>
과 같은 옵션을 주어 컴파일하면 된다
📑 바이트코드란 무엇인가
- 특정 하드웨어나 OS에서 실행되는 기계어가 아닌, 주로 가상 머신에서 실행되는 중간 언어
- 특정 플랫폼을 대상으로 하지 않기 때문에 완전한 기계어보다 유연한 매체
- 완전한 기계어는 아니지만, 고급언어보다는 기계어에 가까운 중간언어
- 고급언어보다 상대적으로 인터프리터에 최적화
- 기계어에 비해 상대적으로 사람이 알아보기 쉽다
컴파일과 실행에서 자바 바이트코드
- 자바 소스 코드를 컴파일하면 자바 바이트코드로 작성된 클래스 파일 생성
- 자바 바이트코드는 완전한 기계어가 아니기 때문에 JVM에서 해석
자바 바이트코드를 보고 싶어요
📑 JIT 컴파일러란 무엇이며 어떻게 동작하는가
자바 바이트코드를 어떻게 실행할까?
- 자바 초기 버전에는 JVM에서 바이트코드를 받아 인터프리터가 모두 한 줄씩 모두 해석
- 완전한 기계어로 컴파일해 사용하는 언어에 비해 실행 속도 아주 느렸다
JIT 컴파일러는 어떻게 동자하나?
- JIT 컴파일러도 일종의 스레드로 컴파일러와 함께 동작
- JIT 컴파일러는 해석 단계에서 수집한 정보를 근거로 해석 최적화
- 인터프리터가 자주 해석하는 부분 등을 기계어로 캐싱해 놓고, 해당 부분이 또 나오면 인터프리터가 해석하지 않고 캐싱해 놓은 기계어를 꺼내 쓴다
📑 JVM 구성 요소
클래스 로더
- 클래스 파일에 대해 동적 로드 수행해, 바이트코드를 메모리에 저장
- 동적 로드: 런타임에 특정 클래스를 처음으로 참조할 때 해당 클래스를 로드, 링크, 초기화한다.
런타임 데이터 영역
- JVM이 실행되며 할당받는 메모리 영역
- 크게 힙 영역, 메서드 영역, 스택 영역, PC 레지스터, 네이티브 메서드 스택으로 구분
- 힙 영역과 메서드 영역은 모든 스레드에 대해 접근할 수 있는 공유 자원
- 나머지 영역은 공유 자원이 아니며, 스레드로부터 안전하다
출저: trek2tech: JVM Internals
1) 메서드 영억
- 클래스 로더로부터 클래스 수준의 정보를 저장
- 클래스 이름, 부모 클래스 이름, 클래스 수준의 정적 변수, 멤버 변수, 메서드, 생성자, 상수풀 등
- 런타임 상수 풀 영역을 포함
2) 힙 영역
- 프로그램 실행 중 생성하는 객체 , 배열 저장
- 가비지 컬렉션의 관리 대상
스택 영역, PC 레지스터, 네이티브 메서드 스택 이미지
출저: trek2tech: JVM Internals
3) 스택 영역
- 스레드 마다 런타임 스택 생성, 스레드 죵료시 제거
- 스택 안에서 메서드 호출 마다 스택 프레임을 쌓아 올리고, 메서드 끝나면 제거
- 스택 프레임은 로컬 변수, 피연산자 스택, 프레임 데이터 부분을 포함
4) PC 레지스터
- 스레드가 시작될 때 각 스레드에 대해 PC 레지스터 생성
- PC 레지스터는 각각 실행할 스레드를 포인터로 가리킨다
5) 네이티브 메서드 스택
- 자바 바이트코드가 아닌 기계어를 실행하기 위한 영역
- 각 기계어를 JNI(Java Native Interface)를 통해 바이트코드로 전환해 저장
실행 엔진
1) 인터프리터
2) JIT 컴파일러
- 해석 단계에서 수집한 정보를 근거로 해석 최적화
- 예를들면 인터프리터가 자주 해석하는 부분 등을 기계어로 캐싱해 놓고, 해당 부분이 또 나오면 인터프리터가 해석하지 않고 캐싱해 놓은 기계어를 꺼내 쓴다
3) 가비지 컬렉터
Native Method Interface
- 네이티브 라이브러리와 상호작용하고 이를 JVM 실행 엔진이 사용할 수 있게 한다
📑 JDK와 JRE의 차이
출저: getkt blog: Difference between JDK, JRE and JVM explained – Java
JRE
- Java Runtime Environment
- 자바 애플리케이션을 실행할 수 있는 환경 제공
- JVM과 런타임 환경, 핵심 라이브러리 포함
JDK
- Java Development Kit
- 자바 애플리케이션을 개발하는 데 필요한 도구 제공
- JRE, 디버깅 도구, 모니터링 도구 등 포함
- 자바 11버전 이후로는 오라클에서 JRE를 따로 제공하지 않고 JDK만 제공