JVM과 친해지기

JVM이 뭐죠?

JVM은 Java Virtual Machine으로 저희가 작성한 .class 자바 코드(애플리케이션)을 동작시켜줍니다.

각 OS에 맞는 JVM이 존재하여 다른 OS 환경에서도 동일하게 자바 코드가 실행되도록 합니다.

JVM은 어떻게 동작하나요?

JVM이 어떻게 동작하는지 보기 전에 JVM 구조를 먼저 살펴보겠습니다.

JVM 구조

크게 Class Loader, Execution Engine, Runtime Data Area로 구성됩니다. 좀 더 자세히 알아보겠습니다.

Class Loader

.class 파일(들)과 CLASSPATH 환경변수로 지정한 jdk/lib에 있는 class 파일들 그리고 .jar 파일까지 모두 JVM 위에 올려놓는 역할을 수행합니다.

Execution Engine

바이트 코드를 명령어 단위로 읽어서 실행하는 역할을 합니다. 여기서 바이트 코드는 Class Loader에 올려진 class 파일들이며 애초에 .java → .class(바이트 코드)로 컴파일한 이유는 JVM이 읽을 수 있게 바이트 코드로 변환한 것이고 실행을 위해서는 기계가 읽을 수 있게 변환하는 작업이 필요합니다.

*바이트 코드는 사람에 더 친숙하다고 볼 수 있습니다.(물론 기계어 보단…친숙하실 겁니다. 무려 hex editor로 바이트 코드를 읽을 수 있다구요!)

기계가 읽을 수 있도록 다시 번역을 해야 하는 작업이 여기서 이루어집니다. 두 가지 방식이 존재합니다.

인터프리터(Interpreter)

흔히 Python, JavaScript 등이 이 방식으로 실행됩니다. 명령어를 하나씩 읽어 해석하고 실행합니다. (한 번만 실행되는 코드는 보통 이 방식으로 진행됩니다.)

인터프리터 방식으로 실행하다가 적절한 시점에 바이트 코드 전체를 컴파일하여 네이티브 코드로 변경하고 이후에는 인터프리팅하지 않고 네이티브 코드로 직접 실행합니다.

JIT(Just-In-Time)

적절한 시점은 이 코드가 자주 실행되는 체크를 하면서 판단하게 됩니다.
또한 네이티브 코드는 캐시에 보관하기에 여러 번 수행되는 코드는 이 방식으로 빠르게 실행시킬 수 있습니다.

Runtime Data Area

JVM이 OS위에서 실행되면서 할당받는 메모리 영역입니다.

PC register / JVM Stack / Native Method / Heap / Method

모든 Thread가 공유하는 영역은 (JVM이 시작될 때 생성되는) Heap과 Method 영역입니다. 그외는 각자 자신만의 영역을 가지고 있습니다. (독립적으로 수행되는 thread를 생각하면 이해가 되실겁니다.)

PC register

현재 수행 중인 JVM 명령 주소가 담겨있습니다.

JVM Stack

Stack Frame이라는 구조체를 저장하는 스텍입니다.

Stack Frame은 메서드가 수행될 때마다 생성되고 종료될 때 제거됩니다. 그 안에는 local variable array, operand stack, reference of runtime constant pool이 담겨있습니다.

Local variable array는 0번째 인덱스에 메서드를 호출한 클래스(this)가 담겨있고 그 이후로는 메서드 파라미터, 지역변수가 담겨있습니다.

Operand stack은 메서드의 호출 결과를 push, pop하면서 실제 작업을 수행합니다.

Reference of runtime constant pool은 현재 실행 중인 메서드가 속한 클래스의 런타임 상수 풀에 대한 참조가 담겨있습니다.

Heap

객체나 인스턴스가 생성되어 저장되는 공간으로 GC의 대상이 되기에 성능 튜닝은 보통 이곳에서 이루어집니다.

Method

JVM이 읽어들인 각각의 클래스와 인터페이스에 대한 런타임 상수 풀, 필드, 메서드의 정보와 static 변수, 메서드의 바이트 코드 등 중요한 정보를 보관하는 영역입니다.

이 중 Runtime Constant Pool 영역이 내부에 따로 존재합니다. 핵심적인 역할을 담당하기 때문입니다. 이곳에서 각 클래스와 인터페이스의 상수뿐만 아니라 메서드와 필드에 대한 모든 레퍼런스까지 담고 있어 이곳을 통해 메소드나 필드의 주소를 찾고 참조를 합니다.

JVM 동작 과정

.java 파일이 javac(자바 컴파일러)를 통해 .class 파일(바이트코드)로 변환되고 이로써 JVM이 읽을 수 있게 됩니다. 이런 class 이외에 필요한 다른 class 들을 Class Loader가 올리면 Execution Engine이 이를 실행시킵니다. Runtime Data Area는 Class Loader와 Execution Engine과 상호작용을 합니다.

[Reference]