[Java] JVM이란 ?

파이 ఇ·2024년 11월 19일
1
post-thumbnail

JVM 이란 무엇인가?

Java Virtual Machine의 줄임말, 직역하면 자바를 실행하기 위한 가상 기계(컴퓨터), 또는 자바 프로그램 환경을 만들어주는 소프트웨어라고 할 수 있다.

Java는 OS에 종속적이지 않다는 특징을 가지고 있는데, OS에 종속 받지 않고 실행되기 위해선 OS 위에서 Java를 실행시킬 무언가를 필요로 하는데 바로 그게 JVM이다.

JVM에서 Java코드 즉, 원시코드(.java)를 실행시키기 위해 Java의 Compiler가 Java 코드를 JVM이 이해할 수 있는 바이트 코드(.class)파일로 변환시켜준다.

변환된 바이트 코드는 기계어가 아니기 때문에 OS에서 바로 실행 시킬 수 없는데, 이 때 JVM이 OS가 바이트 코드를 이해할 수 있도록 해석해준다.

따라서, 바이트 코드는 JVM 위에서 OS에 상관없이 실행될 수 있다. OS에 종속적이지 않고, Java 파일 하나만 만들면 어느 디바이스든 JVM 위에서 실행이 가능하다.

바이트 코드란?

Java 바이트 코드는 JVM이 이해할 수 있는 언어로 변환된 Java 소스코드를 의미한다. Java 컴파일러에 의해 변환된 코드의 명령어 크기가 1바이트라서 Java 바이트코드라고 불리고있다. 바이트코드는 JIT 컴파일러에 의해 바이너리 코드로 변환된다.

📢 바이너리 코드?
바이너리 코드 또는 이진 코드라고 한다. 컴퓨터가 인식할 수 있는 0과 1로 구성된 이진 코드를 의미한다.

즉, CPU가 이해하는 언어는 바이너리 코드, 가상 머신(JVM)이 이해하는 코드는 바이트 코드이다.

바이트 코드를 읽는 방식

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

  • Interpreter 방식
    • 바이트 코드를 한 줄씩 해석, 실행하는 방식이다. 초기 방식으로 속도가 느리다는 단점이 있다.
  • JIT(Just In Time) 컴파일 방식
    • Interpreter의 느린 속도를 보완하기 위해 나온 컴파일 방식.
      바이트 코드를 JIT 컴파일러를 이용해 프로그램을 실제 실행하는 시점(바이트 코드를 실행하는 시점)에 각 OS에 맞는 Native Code로 변환하여 실행 속도를 개선했다. 하지만 바이트 코드를 Native Code로 변환하는데에도 비용이 소모되므로 JVM은 모든 코드를 JIT 컴파일러 방식으로 실행하지 않고, Interpreter 방식을 사용하다 일정 기준이 넘어가면 JIT 컴파일 방식으로 명령어를 실행한다.

JIT(Just In Time) 컴파일러?


기존의 자바는 Interpreter 방식으로 명령어를 하나씩 실행하게끔 이루어져 있어 실행 속도가 느렸지만, 하드웨어가 발전하면서 자바 컴파일러로 JIT 방식으로 개선되어 속도적인 측면에서 상당한 개선을 이루었다.

또한, JIT 컴파일러는 같은 코드를 매번 해석하지 않고, 실행할 때 컴파일 하면서 기계어를 캐싱해버린다. 이후에는 바뀐 부분만 컴파일하고, 나머지는 캐싱된 코드를 사용한다.
이렇게 JIT 컴파일러는 운영체제에 맞게 바이트 실행 코드로 한 번에 변환하여 실행하기 때문에 이전의 Interpreter 방식보다 성능에 10~20배 정도 더 좋다.

JVM의 구성요소

클래스 로더 (Class Loader)

자바는 프로그램이 실행중인 런타임시 동적으로 클래스를 로드하고, jar 파일 내 저장된 클래스들을 JVM 위에 탑재한다. 즉, 클래스를 처음으로 참조할 때 해당 클래스를 로드하고 링크하는 역할을 하는게 클래스 로더이다.

클래스 로더는 .class파일을 묶어서 JVM이 운영체제로부터 할당받은 메모리 영역인 Runtime Data Area로 적재한다.

실행 엔진 (Execution Engine)

클래스 로더에 의해 JVM으로 로드된 .class파일(바이트코드)들은 Runtime Data Area의 Method Area에 배치된다.

이후 JVM은 Method Area의 바이트 코드를 실행 엔진에 제공하여, 로드된 바이트 코드를 실행하는 런타임 모듈이 실행 엔진이다.

실행 엔진은 바이트 코드를 명령어 단위로 읽어서 실행하고, 실제로 JVM 내부에서 기계가 실행할 수 있는 형태로 변경한다.

또한, 실행 엔진은 가비지 컬렉터(GC)를 포함하고 있다.

런타임 데이터 영역 (Runtime Data Area)

런타임 데이터 영역은 JVM의 메모리 영역으로 자바 애플리케이션을 실행할 때 사용되는 데이터들을 적재하는 영역이다.

  • PC Register

    • 각 Thread마다 별도로 존재하며, JVM 명령어의 현재 위치를 가르키는 Register.
  • JVM Stack

    • 각 Thread마다 별도로 존재하며, 메서드 호출 시마다 프레임이 생성된다. 한 프레임마다 지역변수, 파라미터, 리턴 값등이 저장된다.
  • Native Method Stack

    • 자바 이외의 언어(C, C++ 등)로 작성된 네이티브 코드를 실행할 때 사용되는 메모리 영역이다.
  • Heap

    • 인스턴스 또는 객체를 위한 공간으로 GC의 관리 대상이다.
  • Methodn Area

    • 모든 스레드가 공유하는 영역으로, JVM이 로드한 각 클래스 또는 인터페이스에 대한 정보가 저장된다. 이 정보에는 변수, 메서드, static 변수, 인터페이스 등이 포함된다.
    • Runtime Constant Pool
      • 각 클래스와 인터페이스의 상수뿐만 아니라, 메서드와 필드에 대한 모든 레퍼런스까지 담고 있는 테이블이다.
        즉, 어떤 메서드나 필드를 참조할 때 JVM은 런타임 상수 풀을 통해 해당 메서드나 필드의 실제 메모리상 주소를 찾아서 참조한다.

JVM의 동작방식


1. 자바로 개발된 프로그램을 실행하면 JVM은 운영체제로부터 메모리를 할당한다.
2. 자바 컴파일러(javac)가 자바 소스코드(.java)를 자바 바이트코드(.class)로 컴파일한다.
3. 클래스 로더를 통해 JVM 런타임 데이터 영역으로 로딩한다.
4. 런타임 데이터 영역에 로딩된 .class들은 실행 엔진을 통해 해석한다.
5. 해석된 바이트 코드는 런타임 데이터 영역의 각 영역에 배치되어 수행하며 이 과정에서 실행 엔진에 의해 GC의 작동과 Thread 동기화가 이루어진다.

+ 추가로

JRE란?

JRE는 자바 실행 환경(Java Runtime Enviroment)의 약자로 자바로 만들어진 프로그램을 실행시키는데 필요한 라이브러리들과 각종 API, 그리고 JVM이 포함되어 있다.
JRE는 자바로 개발(쓰기)은 안되고 수행(읽기)만 된다.

JDK란?

JDK는 자바 개발 키트(Java Development Kit)의 약자로 이름 그대로 개발자들이 자바로 개발하는데 사용된다.
JDK 안에는 개발 시 필요한 라이브러리들과 javac, javadoc 등의 개발 도구들이 포함되어 있고, 개발을 하려면 당연히 실행도 시켜줘야 하기 때문에 JRE도 함께 포함되어 있다.

📢 정리
Java로 프로그램을 직접 개발하려면 JDK가 필요하고, Java로 만들어진 프로그램을 실행시키려면 JRE가 필요하다.

profile
⋆。゚★⋆⁺₊⋆ ゚☾ ゚。⋆ ☁︎。₊⋆

0개의 댓글