JAVA

[JAVA] JVM(자바 가상머신) 과 메모리 구조

제이미로그 2024. 4. 23. 10:31

📌 1. JVM (Java Virtual Machine) 이란?

 

자바 가상머신 JVM (Java Virtual Machine)은 자바 프로그램 실행환경을 만들어주는 소프트웨어입니다.  자바 코드를 컴파일하여 .class 바이트 코드로 

만들면 이 코드가 자바 가상 머신 환경에서 실행됩니다. JVM은 자바 실행 환경 JRE(Java Runtime Enviorment) 에 포함되어 있습니다. 현재 사용하는 컴퓨터의 운영체제에 맞는 자바 실행 환경 (JRE)가 설치되어 있다면 자바 가상 머신이 설치되어 있다는 뜻입니다. 

 

"JVM을 사용함으로써 하나의 바이트 코드(.class)로 모든 플랫폼에서 동작하도록 할 수 있습니다. .class 파일은 바이트코드라고 하는데 사람이 쓰는 자바 코드에서 컴퓨터가 읽는 기계어로의 중간 단계라고 생각하시면 됩니다. "

 

 

Java의 경우 Java 언어로 작성된 Tes.java는 컴파일하면 Test.class 파일이 생성됩니다. 그리고 이렇게 생성된 바이트 코드는 각자의 플랫폼에 설치되어

있는 자바 가상 머신(JVM)이 운영체제에 맞는 실행 파일로 바꿔줍니다. 즉 Java에서는 C언어와 달리 JVM을 사용하기 때문에 각자의 플랫폼에 맞게끔 컴파일을 다르게 할 필요가 없습니다. 하나의 바이트 코드로 JVM이 설치되어 있는 모든 플랫폼에서 동작이 가능합니다.

 

* Java는 플랫폼에 종속적이지 않지만 JVM은 플랫폼에 종속적이다.

: 리눅스의 JVM과 윈도우의 JVM은 서로 다르다. 자바로 작성된 모든 프로그램은 자바 가상 머신에서만 실행될 수 있으므로, 자바 프로그램을 실행하기 위해서는 반드시 자바 가상 머신이 설치되어 있어야 합니다. 우리가 자바로 .java 코드를 작성하고 파워셀이나 터미널에 있는 자바 컴파일러인 javac에 컴파일 명령을 내리면 .class 파일이 만들어집니다. 이후에 바이트 코드는 클래스 로더를 통해 JVM Runtime Data Area로 로딩되고 로딩된 .class 바이트 코드를 실행할 컴퓨터에 깔린 JVM에 가져다주면 그 컴퓨터가 이 프로그램을 실행할 때 JVM이 기계어로 해석합니다. 

 

📌 2. 바이트 코드를 읽는 방식?

 

JVM은 바이트코드를 명령어 단위로 읽어서 해석하는데, Interpreter 방식과 JIT 컴파일 방식 두 가지 방식을 혼합하여 사용합니다. Interpreter 방식은

바이트코드를 한 줄씩 해석, 실행하는 방식입니다. 초기 방식으로 속도가 느리다는 단점이 있습니다. 

 

이렇게 느린 속도를 보완하기 위해 나온 것이 JIT(Just In Time) 컴파일 방식입니다. 바이트코드를 JIT 컴파일러를 이용해 프로그램을 실제 실행하는 시점(바이트코드를 실행하는 시점)에 각 OS에 맞는 Native Code로 변환하여 실행 속도를 개선하였습니다. 하지만, 바이트코드를 Native Code로 변환하는 데에도 비용이 소요되므로, JVM은 모든 코드를 JIT 컴파일러 방식으로 실행하지 않고 인터프리터 방식을 사용하다가 일정 기준이 넘어가면 JIT 컴파일 방식으로 명령어를 실행합니다. 

 

📌 3. JIT (Just In Time) 컴파일러란?

 

기존의 자바는 인터프리터 방식으로 명령어를 하나씩 실행하게끔 이루어져 있어 실행 속도가 느렸습니다. 하지만 하드웨어가 발전하면서 자바 컴파일러에도 JIT 컴파일러 방식으로 개선되어 속도적인 측면에서 상당한 개선을 이루었습니다. JVM은 JIT(Just In Time) 컴파일러라고 합니다. 또한 JIT 컴파일러는 같은 코드를 매번 해석하지 않고, 실행할 때 컴파일을 하면서 해당 코드를 캐싱해버립니다. 이후에는 바뀐 부분만 컴파일하고 나머지는 캐싱된 코드를 사용합니다. 이렇게 JIT 컴파일러는 운영체제에 맞게 바이트 실행 코드로 한번에 변환하여 실행하기 때문에 이전의 자바 해석기(Java interpreter) 방식보다 성능이 10~20 배 정도 더 좋습니다. 

 

📌 4. 자바 가상 머신(JVM) 의 동작 방식?

 

1. 자바로 개발된 프로그램을 실행하면 JVM은 OS로부터 메모리를 할당합니다. 

2. 자바 컴파일러(javac) 가 자바 소스 코드(.java)를 자바 바이트 코드(.class)로 컴파일 합니다. 

3. class Loader를 통해 JVM Runtime Data Area로 로딩합니다.

4. Runtime Data Area에 로딩된 .class 들은 Execution Engine을 통해 해석합니다.

5. 해석된 바이트 코드는 Runtime Data Area의 각 영역에 배치되어 수행하며 이 과정에서 Execution Enginee에 의해 GC의 작동과 스레드 동기화가 

이루어집니다. 

 

📌 4. JVM 의 구조?

 

클래스 로더(Class Loader)

 

 

자바는 동적으로 클래스를 읽어오므로, 프로그램이 실행중인 런타임에서야 모든 코드가 자바 가상 머신과 연결됩니다. 이렇게 동적으로 클래스를 로딩해주는 역할을 하는 것이 클래스 로더(Class Loader)입니다. 자바에서 소스를 작성하면 .java 파일이 생성되고 .java 소스를 컴파일러가 컴파일하면 .class 파일이 생성되는데 클래스 로더는 .class 파일을 묶어서 JVM이 운영체제로부터 할당받은 메모리 영역인 Runtime Data Area로 적재합니다. 

 

실행 엔진(Execution Engine)

 

클래스 로더에 의해 JVM으로 로드된 .class 파일(바이트코드)들은 Runtime Data Areas의 Method Area에 배치되는데, 배치된 이후에 
JVM은 Method Area의 바이트 코드를 실행 엔진(Execution Engine)에 제공하여, 정의된 내용대로 바이트 코드를 실행시킵니다. 이 때 로드된 바이트코드를 실행하는 런타임 모듈이 실행 엔진(Execution Engine)입니다. 실행 엔진은 바이트코드를 명령어 단위로 읽어서 실행합니다. 

 

가비지 컬렉터(Garbage Collector)

 

자바 가상 머신은 가비지 컬렉터(Garbage Collector)를 이용하여 더는 사용하지 않을 메모리를 자동으로 회수해줍니다.  따라서 개발자가 따로 메모리를 관리하지 않아도 되므로, 더욱 손쉽게 프로그래밍을 할 수 있도록 도와줍니다. Heap 메모리 영역에 생성(적재)된 객체들 중에 참조되지 않은 객체들을 탐색 후 제거하는 역할을 하며 해당 역할을 하는 시간은 정확히 언제인지를 알 수 없습니다. GC 역할을 수행하는 스레드를 제외한 나머지 모든 스레들은 일시정지상태가 됩니다.