Notice
Recent Posts
Recent Comments
Link
«   2025/03   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31
Archives
Today
Total
관리 메뉴

장미정원

✨ JVM 가상머신 본문

Back-end

✨ JVM 가상머신

신희성 2024. 5. 28. 15:59

JVM

자바를 실행하기 가상 환경을 만들어주는 소프트웨어입니다. (Java Virtual Machine)

 

일반적인 컴파일

C나 C++같은 언어로 코드 작성 후 컴파일을 하면 컴파일된 결과물을 다른 OS에서 실행하려하면 동작하지 않습니다. 동일한 OS에서 컴파일과 실행을 한다면 프로그램은 문제 없이 동작하지만, 컴파일 후 다른 OS에서 해당 프로그램을 실행하려하면 동작하지 않습니다. 이렇게 일반적인 컴파일 과정은 OS에 종속적이기 때문에 다른 OS에서 동작하는 프로그램을 컴파일 하려면 원하는 OS마다 크로스 컴파일이라는 추가 작업을 해야합니다.

일반 어플리케이션

JVM

Java 바이트 코드는 OS에 상관없이 JVM 안에서 컴파일되고 동작합니다. 그렇게 때문에 자바 언어는 OS에 종속적이지 않은 언어입니다.

이렇게 자바 어플리케이션은 OS에 종속적이기 않게 되지만 JVM은 OS에 종속적인 형태가 됩니다. 다시말해 각 OS에 맞는 JVM이 필요합니다.

JVM 어플리케이션

 

JDK, JRE, JVM

JDK - Java Development Kit

자바 개발 환경으로 자바 어플리케이션을 개발하기 위한 도구를 제공합니다.

자바 언어를 자바 바이트 코드로 컴파일 해주는 도구인 javac, 자바 클래스 파일을 해석해주는 javap 등이 있습니다.

 

JRE - Java Runtime Environment

자바 실행 환경으로 JVM, 자바 클래스 라이브러리, 자바 어플리케이션 실행에 필요한 파일들을 제공합니다.

 

JVM - Java Virtual Machine

자바 바이트코드를 실행시키는 역할을 합니다. 

 

JVM은 JRE의 한 부분으로 JRE는 JVM을 포함하며 자바 어플리케이션 실행에 필요한 라이브러리들을 제공합니다.

 

JDK - JRE - JVM

 

 

JVM 구조

JVM은 자바 바이트 코드를 해석해주는 역할을 합니다.

자바 컴파일러는 자바 소스 파일을 자바 바이트코드로 변환시켜 줍니다. 

 

JVM의 구조는 크게 Class Loader, Runtime Data Area, Execution Engine 이렇게 3가지로 구성되어 있습니다.

 

JVM

 

Class Loader

런타임 시점에 클래스를 동적으로 Runtime Date Area의 메모리에 적재(로드)하는 역할을 합니다.

동적으로 로드한다는 말은 런타임 시점에 바이트코드를 실행시키면서 특정 클래스 로드가 필요할 때마다 Class Loader가 메모리에 적재하는 것입니다. 클래스가 로드될 때는 로드 -> 링킹(검증) -> 초기화 과정을 거쳐 Runtime Date Area의 메모리에 로드됩니다.

 

Execution Engine

메모리에 적재된 클래스들을 기계어로 변경하여 명령어 단위로 실행시켜주는 역할을 합니다. Execution Engine은 Interpreter, JIT, Garbage Collector 이렇게 세가지 부분으로 구성되어 있습니다. 바이트코드를 해석할 때는 명령어를 한줄 한줄 읽고 실행하는 인터프리터 방식과 JIT 방식 두가지를 혼합하여 사용합니다.

 

인터프리터 방식은 바이트코드를 한줄 한줄 해석하여 실행하는 방식이라 다른 네이티브 언어의 실행보다 속도가 느리고 같은 코드를 반복해서 해석하게 되는 단점이 있습니다. 이러한 단점을 보안하기 위해 JIT 컴파일 방식을 사용합니다.

 

JIT - Just In Time

JIT 컴파일러는 실행 시점에 인터프리터와 같이 바이트코드를 읽어 기계어로 변환하다가 반복되거나 자주 사용되는 코드를 발견하면 적절한 시점에 해당 바이트코드를 컴파일하여 네이티브 코드로 변환하여 캐싱합니다. JIT 컴파일러는 코드가 실행되는 과정에 실시간으로 일어나며 자주 실행되는(반복되는) 코드는 컴파일하여 네이티브 코드로 캐싱해두어 반복되는 코드를 다시 해석하지 않고 컴파일된 네이티브 코드를 바로 사용하여 성능을 향상시켰습니다.

 

Runtime Data Area

JVM이 운영체제로 부터 할당 받은 메모리 영역이며 총 다섯개의 영역으로 구성되어 있습니다.

 

Method Area

  • 클래스 맴버 변수, 메서드 정보, 타입 정보, static/final 변수 등에 대한 정보가 올라오는 영역입니다.
  • 모든 쓰레드가 공유합니다.

 

Heap Area

  • new 키워드로 생성한 인스턴스가 올라오는 영역입니다. GC의 대상이 되는 영역입니다.
  • Heap 영역의 인스턴스에 접근하기 위해서는 Stack에 저장되어있는 레퍼런스를 통해 접근할 수 있습니다.
  • Heap 영역이 가득 찬다면 OutOfMemoryError (OOM)이 발생합니다.
  • 모든 쓰레드가 공유합니다.

 

Stack

  • 지역변수, 매개변수, return 값 등같이 임시적으로 사용되는 값들이 올라오는 영역입니다.
  • 메서드가 실행되면 Stack 영역의 해당 메서드의 프레임을 push 하여 데이터를 저장하고 메서드가 종료될 때 Stack에서 해당 메서드의 프레임을 pop합니다.
  • 프레임은 Stack에 저장되는 데이터의 자료구조입니다.

 

PC register

  • 현재 쓰레드가 실행되는 부분의 주소와 명령을 저장하는 영역입니다.

 

Native Method Stack

  • Native 언어로 작성된 코드를 실행시키기 위한 영역입니다.

 

Runtime Data Area

 

위의 그림과 같이 Stack, PC register, Native Method Stack은 쓰레드가 생성될 때마다 각 쓰레드에 새로 할당되고 Method Area, Heap 영역은 JVM이 시작될 때 할당되어 모든 쓰레드가 공유합니다. 각각의 쓰레드는 다른 쓰레드에게 할당된 메모리 영역에 접근할 수 없습니다. (지역 변수의 동시성 문제 고려X)