학습한 내용을 정리한 포스팅입니다. 직역된 부분이 있어 어색할 수 있습니다. 틀린 부분이 있다면 언제든 지적 해주시면 감사하겠습니다. 🙇🏻♀️
Java Virtual Machine
이름 그대로 해석한다면 자바 가상 머신.
자바는 WORA(Write Once Run Anywhere)
라는 컨셉을 가지고 가상 머신 위에서 실행되게끔 개발 되었다.
현실에서 소프트웨어가 운영체제를 통해 컴퓨터 하드웨어 상에서 구동 되듯이 자바 개발 도구인 JDK를 이용해 개발된 프로그램은 JRE에 의해 가상의 컴퓨터인 JVM에서 구동된다. (컴파일된 바이트 코드는 JRE에 의해서 실행되고 JRE는 JVM에서 구동.)
위의 그림과 같이 JVM은 세 파트로 나뉘게 된다.
- ClassLoader Subsystem, Runtime Data Area, Execution Engine
.class
파일로 저장된다. 소스코드가 바이트코드로 변환되는 동안 컴파일러는 아래의 과정들을 거친다.Step 1: Parse: Reads a set of *.java source files and maps the resulting token sequence into AST (Abstract Syntax Tree)-Nodes.
Step 2: Enter: Enters symbols for the definitions into the symbol table.
Step 3: Process annotations: If Requested, processes annotations found in the specified compilation units.
Step 4: Attribute: Attributes the Syntax trees. This step includes name resolution, type checking and constant folding.
Step 5: Flow: Performs dataflow analysis on the trees from the previous step. This includes checks for assignments and reachability.
Step 6: Desugar: Rewrites the AST and translates away some syntactic sugar. (Syntactic sugar: 코드에는 영향을 끼치지 않으면서 인간이 읽고쓰기 편하게 만들어 놓은것.)
Step 7: Generate: Generates ‘.Class’ files.
컴파일러에 의해 생성된 클래스 파일들은 머신 또는 OS에 독립적이라 어느 시스템에서도 실행될 수 있다.
실행을 위해서는 메인 클래스 파일은 JVM을 통과하게 되고 아래 세 개의 메인 스테이지를 통과하면서 실행된다.
ClassLoader
메인 클래스는 메모리에 로드된다. 이 밖의 다른 모든 클래스들은 클래스 로더에 로드 된다.
그 자체로 객체인 클래스 로더는 클래스 바디의 flat namespace를 만든다.
// loadClass function prototype
Class r = loadClass(String className, boolean resolveIt);
// className: name of the class to be loaded
// resolveIt: flag to decide whether any referenced class should be loaded or not.
Bytecode Verifier
클래스의 바이트 코드가 클래스 로더에 로드된 이후에 Bytecode Verifier에 의해 점검을 받는다.
아래와 같은 내용들을 점검한다
위에서 살펴본 실행하는 방법에도 등장한, 실행 마지막 단계에 만나게 되는 컴파일러다.
Just-In-Time Complier. JRE에서 중요한 부분이다. 런타임시 어플리케이션 퍼포먼스 최적화와 관계가 있다.
JIT 컴파일러를 사용할 때 (JVM이 같은 시퀀스의 바이트 코드를 반복해서 해석하는 것과는 다르게) 하드웨어는 네이티브 코드를 실행할 수 있다. 이를 통해 실행 시 속도가 더 빠를 수 있다.
JVM은 각 메소드에 대해 사전 정의된 컴파일 임계값에서 시작되고 해당 메소드가 호출될 때마다 감소되는 호출 개수를 유지보수 하는데 이 때 호출 개수가 0에 도달하면 해당 메소드에 대한 JIT 컴파일이 트리거 됨. -> 자주 쓰는 메소드는 JVM이 시작된 직후 컴파일 되며 자주 사용되지 않는 메소드는 훨씬 나중에 컴파일 되거나 전혀 컴파일되지 않음.
컴파일러는 소스코드나 자바 프로그램을 바이트코드(또는 기계어)로 변환하고 인터프리터는 시스템에서 바이트코드를 실행한다. 이 인터프리터는 JVM이라고 불린다. 바이트 코드는 컴파일러와 인터프리터 사이의 공통된 조각(common piece)이다.