본문 바로가기
Java

[Java] JVM 구조 이해하기, JVM을 알아야 하는 이유

by doogfoot 2024. 11. 15.

Java 개발자라면 누구나 들어봤을 JVM에 대해, 공부해야 되는 이유와 그 구조에 대해서 정리해 봤습니다.

 

JVM(Java Virtual Machine)은 Java로 개발된 프로그램을 실행하기 위한 가상 머신입니다.

 

구성 요소는 다음과 같습니다

 

 

1. 클래스 로더 (Class Loader)

2. 런타임 데이터 영역 (Runtime Data Area = JVM Memory)

3. 실행 엔진 (Execution Engine)

4. 네이티브 인터페이스 (Java Native Interface, JNI)

5. 네이티브 메서드 라이브러리 (Native Method Libraries)

 

 

 

1. 클래스 로더 

 

Java의 클래스(. class) 파일을 메모리에 로드하고 필요한 메타 데이터를 관리하고 있습니다.

 

클래스 로더는 클래스의 로드(Load), 연결(Link), 초기화(Initialization) 과정을 통해 실행을 준비하게 됩니다.

 

즉, 클래스 파일을 가져와 메모리에 로드하고, 클래스에 문제가 없는지 검증하고, 변수를 적절한 값으로 초기화하는 과정을 말합니다.

 

클래스 로더의 구조나 로딩 과정은 내부적으로 알아서 해주기 때문에 개발자가 단순한 앱 개발을 한다면  클래스 로더에 대해 잘 몰라도 상관없을 수 있습니다.

 

하지만 개발을 하다 보면 클래스 로더를 직접 조작하거나 이해하고 있어야만 개발할 수 있는 상황이 오기 때문에 공부를 해두면 좋을 것 같습니다.

 

 

 

2. 런타임 데이터 영역

 

위의 JVM 구조 그림에서 JVM Memory라고 표시된 영역으로, JVM 이 실행 중일 데이터를 저장하는 다양한 메모리 영역을 말합니다.

 

메서드 영역 : 클래스의 메타 데이터, 상수, static 변수, 메서드 코드 등 이 저장됩니다

JVM 메모리에 정적 영역으로 모든 스레드가 공유하는 메모리 공간입니다.

 

힙 영역 : 객체와 배열이 저장되는 공간으로 가비지 컬렉터가 관리합니다. 모든 스레드가 공유하는 영역입니다

 

스택 영역 : 각 스레드마다 독립적으로 할당되는 공간으로, 메서드 호출시마다 프레임이 생성되어 지역 변수, 매개 변수등이 저장됩니다.

 

PC(Program Counter) 레지스터 : 각 스레드별로 하나씩 존재하며, 현재 실행 중인 JVM 명령의 주소를 저장합니다

 

네이티브 메서드 스택 : 자바 외부의 네이티브 코드(C/C++)를 실행할 때 사용되는 메모리 영역입니다.

 

Java에서 객체, 변수, 상수 등의 요소별로 어떤 메모리 영역에 저장되는지, 스레드별로 독립적인지를 알고 개발하면 좀 더 안정적이고 고성능의 프로그램을 개발할 수 있습니다.

 

 

3. 실행 엔진

 

메모리에 배치된 바이트 코드를 읽어서 실제로 실행하는 영역입니다.

 

바이트 코드를 컴퓨터가 실행할 수 있는 기계어로 변환하여 실행합니다.

 

이때 바이트 코드를 실행시키는 방법에 따라 크게 두 가지로 방식으로 분류할 수 있습니다.

 

1) 인터프리터(Interpreter)

 

바이트 코드를 한 줄씩 읽어서 기계어로 해석하고 실행합니다.

 

JVM에서 바이트 코드는 기본적으로 인터프리터 방식으로 코드를 실행합니다.

 

기계어로 변환하는 시간이 있기 때문에 미리 기계어로 변환되어 있는 언어(C/C++)에 속도가 느린 편입니다. 

 

또한 같은 메서드를 여러 번 호출하더라도 매번 해석하고 실행하기 때문에 비효율적인 측면도 있습니다.

 

2) JIT (Just-In-Time) 

 

인터프리터의 속도, 효율 이슈를 해결하기 위해 도입된 방식입니다.

 

자주 반복하여 실행되는 코드를 런타임 중에 기계어로 컴파일하고 재사용하게 됩니다.

 

한번 기계어로 컴파일하여 캐싱 한 코드를 실행하기 때문에 속도가 비교적 빠른 편입니다.

 

 

두 방식의 장단점이 있기 때문에 인터프리터 방식을 사용할지 JIT 방식을 사용할지는 컴파일 임계치라는 것을 계산하여 결정합니다

 

메서드가 호출된 횟수와 메서드가 종료된 횟수를 통해 컴파일 임계치를 계산하고 이를 초과하면 JIT 방식으로 기계어를 캐싱하게 됩니다.

 

 

실행 엔진에는 가비지 콜렉터(GC)가 포함되어 있습니다. 

가비지 콜렉터는 자동으로 메모리를 관리해 주는 JVM 기능입니다.

Heap 메모리 영역에서 더 이상 사용되지 않거나 오래된 메모리를 정리하여 메모리 효율을 증가시킵니다.

C언어에서 메모리 해제를 통해 개발자가 직접 메모리를 관리한 것과 다르게 Java에서는 자동으로 관리가 가능합니다.

GC의 상태에 따라 애플리케이션의 성능이나 장애가 발생할 수도 있으니 구조나 동작 방식을 미리 공부하면 좋을 것 같습니다.

 

 

 

4. 네이티브 인터페이스 (JNI)

 

JVM 외부의 네이티브 코드를 호출하고 실행할 수 있도록 연결하는 역할을 합니다.

 

C, C++ 등으로 작성된 라이브러리를 실행할 때 주로 사용합니다.

 

Java를 통해 실행할 수 없는 하드웨어, OS의 기능에 접근할 수도 있습니다

 

 

5. 네이티브 메서드 라이브러리 

 

JVM 이 위에서 설명한 네이티브 코드를 실행할 때 사용하는 라이브러리 파일들을 말합니다

 

 

 

 

정리

 

오늘은 JVM의 구조와 그 요소들에 대해 간단히 정리해봤습니다

JVM은 Java로 개발된 애플리케이션을 실행하기 위한 가상 머신이라고 간단히 정의할 수 있습니다.

JVM의 구성 요소인 클래스로더, 메모리 공간, 실행 엔진 등을 더 자세하게 공부해 두면 개발할 때 반드시 도움이 될 것이라고 생각합니다.

 

댓글