JVM의 구조
먼저 자바 컴파일러부터 살펴보자

.java 형식의 자바 코드는 자바 컴파일러를 거쳐 .class 형식의 바이트 코드로 컴파일된다. 하나의 자바 프로젝트에 여러 개의 .java 파일이 있을 수 있다. 그러면 이들 모두가 하나의 .class 파일로 합쳐질까? 아니다. 각각의 .java 파일은 각각의 .class 파일로 컴파일된다.

그렇게 분할된 .class 파일들은 JVM으로 진입하게 된다.

그림에서 보이듯, JVM은 크게 ClassLoader, Runtime Data Area, Execution Engine, 3가지 서브시스템들로 구성된다.
각각이 무슨 역할을 하는지 알아보자.
1. ClassLoader
- Bootstrap classloader: bootstrap classpath에 존재하는 클래스들을 로드한다.
- Extension classloader: ext 폴더 내에 존재하는 클래스들을 로드한다.
- Application classloader: 어플리케이션 레벨 classpath를 로드한다.
- Verify: 생성된 바이트코드가 적절한지 그렇지 않은지 검증한다. 만약 적절하지 않다고 판단되면 verification error를 발생시킨다.
- Prepare: 모든 정적 변수들을 위한 메모리가 할당되고 디폴트 값이 할당된다.
2. Runtime Data Area
- Method Area: 정적 변수를 포함한 모든 클래스 레벨 데이터들이 이곳에 저장된다. JVM 인스턴스 하나당 하나의 method area만 존재한다. 따라서 여러 스레드에 의해 공유되는 자원이다.
- Heap Area: 모든 객체와 그 인스턴스 변수들이 이곳에 저장된다. method area와 마찬가지로, JVM 인스턴스 당 하나의 heap area만 존재한다. 공유 자원이기 때문에, thread-safe하지 않다.
- Stack Area: 각 스레드마다 런타임 스택이 생성된다. 모든 지역 변수들 또한 스택 메모리에 생성된다. 공유 자원이 아니기 때문에 thread-safe하지 않다.
- PC Registers: 각 스레드는 별도의 PC register를 갖는다.
3. Execution Engine
- 생성된 바이트코드가 runtime data area에 할당되면, execution engine에 의해 실행된다.
- Interpreter: 바이트코드를 interpret한다. 같은 method가 여러 번 호출되어도 계속 반복적으로 Interpret하기 때문에 성능 저하가 발생한다.
- JIT Compiler: 반복된 코드를 발견하면 캐싱하는 방식으로 Interpreter의 단점을 보완한다.
- Garbage Collector: 객체들이 생성될 때 그 참조를 저장하고 있다가 그 객체가 더 이상 누구에게도 참조되지 않으면 해당 객체를 제거한다.