Post

[Java] JVM

[Java] JVM

📌 JVM이란?

JVM(Java Virtual Machine) 은 자바 애플리케이션을 실행하기 위한 가상 머신으로 자바 바이트코드(.class)를 운영 체제와 하드웨어에 독립적으로 실행할 수 있도록 하는 소프트웨어 계층이다. 즉, JVM이 설치된 OS에서는 별도의 작업 없이 자바로 작성된 프로그램(.java)을 실행할 수 있다.

📌 동작 방식

image.png

  1. 자바 소스 파일(.java)는 자바 컴파일러(javac)에 의해 바이트코드(.class)로 변환된다.
  2. 변환된 바이트코드는 JVM의 클래스 로더에 의해 메모리로 로드된다.
  3. 클래스 로더가 로드한 바이트코드는 런타임 데이터 영역에 배치된다.
  4. 실행 엔진이 바이트코드를 해석하거나 JIT 컴파일러를 통해 기계어로 변환하여 실행한다.
  5. 실행 도중 필요 없는 객체는 가비지 컬렉터가 정리한다.

📌 구성 요소

Class Loader

  • Loading

JVM이 .class 파일을 찾고 이를 런타임 데이터 영역에 적재한다. 또한 심볼릭 레퍼런스(필드, 메소드 등)를 런타임 상수 풀에 복사한다.

  • Linking

로드된 클래스가 실제로 실행될 수 있도록 준비하는 과정으로 다음 하위 단계로 나뉜다.

  • Verification(검증): 클래스 파일의 바이트코드가 JVM 명세에 맞는지 확인한다. 구조적으로 올바르지 않는 경우 VerifyError 가 발생한다.
  • Preparation(준비): 클래스의 static 필드를 위한 메모리를 할당하고 기본값으로 초기화한다. 명시적 초기화는 아직 이루어지지 않는다.
  • Resolution(해결): Loading 단계에서 복사한 심볼릭 레퍼런스를 실제 메모리 주소로 변환한다.
  • Initialization

클래스의 <clinit> 메소드가 호출되어 static 변수의 명시적 초기화와 static 블록이 한 번만 실행된다.

Execution Engine

Execution Engine에서 바이트코드가 실제로 실행된다.

  • Interpreter

JVM이 바이트코드를 명령어 단위로 하나씩 읽고 해석하여 실행하는 방식이다. 이 방식은 JVM이 시작될 때 빠른 실행이 가능하도록 하지만, 같은 메소드가 반복적으로 호출될 때마다 매번 해석해야 하므로 실행 속도가 느리다.

  • JIT(Just-In-Time) Compiler

Interpreter의 성능 저하 문제를 보완하기 위해 도입되었다. JVM은 처음에는 Interpreter 방식으로 코드를 실행하다가 특정 메소드가 반복적으로 호출되는 Hotspot 이 감지되면 해당 바이트코드 전체를 기계어로 컴파일한다. 이후 기계어를 캐시에 저장한 후, 같은 메소드가 다시 호출될 때 캐싱된 기계어를 읽어 바로 실행한다.

  • Garbage Collector

JVM의 실행 엔진에서 사용하지 않는 객체를 탐지하여 힙 메모리에서 제거한다. 이를 통해 메모리 누수 문제를 해결할 수 있다.

Runtime Data Area

자바 프로그램 실행 시 운영 체제로부터 할당받는 메모리 영역으로 프로그램의 데이터와 실행 정보를 저장하고 관리한다.

  • PC Register

각 쓰레드마다 독립적으로 생성되는 메모리 공간으로 현재 실행 중인 JVM 명령어의 주소를 저장한다. 이 영역은 쓰레드가 시작될 때 할당되고 종료 시 해제된다.

  • JVM Stacks

각 쓰레드마다 독립적으로 생성되는 메모리 공간으로 메소드 호출 시마다 스택 프레임이 쌓인다. 각 스택 프레임에는 메소드의 지역 변수, 매개변수, 리턴값, 연산 중간값 등이 저장된다. 메소드가 호출되면 프레임이 push되며, 종료되면 pop된다. 스택 구조이므로 LIFO 구조로 동작하며 스택 크기를 초과하면 StackOverflowError 가 발생한다. 이 영역은 쓰레드 간 공유하지 않는다.

  • Native Method Stacks

자바가 아닌 네이티브 코드(C++, C, 어셈블리 등)로 작성된 메소드를 실행할 때 사용하는 스택이다. 역시 각 쓰레드마다 독립적으로 할당되며 자바 애플리케이션이 JNI(Java Native Interface) 를 통해 네이티브 메소드를 호출할 때 이 스택이 활용된다.

  • Heap

JVM이 시작될 때 생성되며 모든 쓰레드가 공유하는 메모리 공간이다. new 키워드를 통해 동적으로 생성된 객체 또는 배열이 저장된다. 객체의 실제 데이터는 힙에 저장되고 참조값은 JVM Stack에 저장된다. 힙은 가비지 컬렉션의 대상이 된다. 힙이 가득 차면 OutOfMemoryError 가 발생한다.

  • Method Area

클래스에 대한 모든 정보(메타데이터, 바이트코드, 상수 풀, static 변수 등)가 저장되는 영역으로 모든 쓰레드가 공유한다. JVM이 클래스를 로딩하면 그 정보가 메소드 영역에 적재된다. 이 영역은 JVM이 시작되면 생성되며 프로그램이 종료될 때까지 유지된다. 이 영역이 가득 차면 OutOfMemoryError 가 발생한다.

📌 JDK vs. JRE vs. JVM

image.png

JDK(Java Development Kit)

JDK자바 애플리케이션을 개발하기 위한 소프트웨어 개발 키트이다. JDK에 JRE, 컴파일러, 디버거, JavaDoc 등이 포함되어 있다. 즉, 개발자는 JDK를 사용하여 자바 소스 코드를 작성하고 컴파일 및 디버깅할 수 있다.

JRE(Java Runtime Environment )

JRE자바 프로그램을 실행할 수 있는 환경을 제공하는 패키지이다. JVM과 프로그램 실행에 필요한 라이브러리, 리소스 파일 등을 포함한다. 컴파일러, 디버거같은 개발 도구는 포함하지 않으므로 자바 애플리케이션을 개발할 수는 없다.

JVM(Java Virtual Machine)

JVM 은 자바 바이트코드(.class)를 운영 체제와 하드웨어에 독립적으로 실행할 수 있도록 하는 소프트웨어 계층이다. 즉, JVM이 설치된 OS에서는 별도의 작업 없이 자바로 작성된 프로그램(.java)을 실행할 수 있다. JVM은 JRE에 포함되며 개발자가 직접 JVM을 다루는 것이 아닌 자바 프로그램이 실행되면 내부적으로 동작한다.

즉, JDK 안에 JRE가 있고, JRE 안에 JVM이 있다.

📌 참고

https://inpa.tistory.com/entry/JAVA-%E2%98%95-JVM-%EB%82%B4%EB%B6%80-%EA%B5%AC%EC%A1%B0-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EC%98%81%EC%97%AD-%EC%8B%AC%ED%99%94%ED%8E%B8#%EC%9E%90%EB%B0%94%EA%B0%80%EC%83%81%EB%A8%B8%EC%8B%A0jvm%EC%9D%98%EB%8F%99%EC%9E%91%EB%B0%A9%EC%8B%9D

https://lsj31404.tistory.com/105

https://inpa.tistory.com/entry/JAVA-%E2%98%95-JDK-JRE-JVM-%EA%B0%9C%EB%85%90-%EA%B5%AC%EC%84%B1-%EC%9B%90%EB%A6%AC-%F0%9F%92%AF-%EC%99%84%EB%B2%BD-%EC%B4%9D%EC%A0%95%EB%A6%AC

https://www.boardinfinity.com/blog/understanding-the-difference-between-jdk-jre-and-jvm/

This post is licensed under CC BY 4.0 by the author.