Java 코드의 동작 원리와 JVM 구성 요소

kindof

·

2026. 2. 5. 23:34

블로그 초창기에 썼던 글이 있긴한데 간단하게 다시 Java 코드의 동작 원리와 JVM에 대해 정리해본다.
https://studyandwrite.tistory.com/59

 

 

1. Java 코드 동작

아래와 같은 Java 코드가 있다.

public class Main {
    public static void main(String[] args) {
        System.out.println("Hello");
    }
}

 

코드 자체는 CPU가 이해할 수 없는 텍스트이기 때문에 Java 컴파일러(javac)는 이 소스 코드를 컴파일하여 .class 파일을 생성한다.

javac Main.java

 

.class 파일에는 JVM이 이해할 수 있는 바이트코드가 들어가있으며 한 번 컴파일되면 JVM 위에서 어디서든 실행 가능하게 된다.

Compiled from "Main.java"
public class Main {
  public Main();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: ldc           #3                  // String Hello
       5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       8: return
}

 

그리고 최종적으로 코드 실행 시에는 아래와 같이 JVM이 .class 파일을 읽어서 실행하게 된다.

java Main

 

 


 

 

2. JVM 구성 요소

JVM은 Java 바이트코드를 실행하기 위한 가상 실행 환경으로 Java 프로그램이 OS 위에서 실행되는 게 아니라 JVM 위에서 실행되는 구조다.

 

JVM의 내부는 크게 아래 요소로 구성된다.

 

1) Class Loader(클래스 로더)

클래스 로더는 필요한 클래스들을 메모리에 올리는 역할을 한다.

Loading, Linking, Initialization 단계가 있는데, java는 런타임에 클래스를 참조하고 클래스를 처음으로 참조할 때 해당 클래스를 로드하고 링크하는 것이 클래스 로더가 하는 일이다.

 

2) Runtime Data Area(메모리 영역)

JVM은 실행 중 메모리를 여러 영역으로 나누는데 Method Area, Heap, Stack, PC Register 등이 있다.

 

3) Execution Engine(실행 엔진)

실행 엔진은 바이트코드를 실제로 실행하는 핵심 엔진이다. 실행 방식은 크게 두 가지가 존재한다.

  • Interpreter 방식은 바이트코드를 한 줄씩 해석해서 실행하는 방식으로 빠르게 시작하지만 느리다는 단점이 있다.
  • JIT Compiler (Just-In-Time) 방식은 자주 실행되는 코드를 기계어로 변환해서 캐싱하여 반복 실행 시 매우 빠르게 성능 최적화를 한다. JIT Compiler가 컴파일하는 과정은 바이트코드를 Interpreting 하는 것보다 오래 걸리기 때문에, 내부적으로 해당 메서드가 얼마나 자주 수행되는지 체크하고 일정 정도를 넘을 때만 컴파일을 수행한다.

Interpreter, JIT Compiler는 동시에 함께 쓰이는 것으로 역할 분담을 하는 것으로 이해하고 있어야 한다.

 

프로그램 시작 시점에는 어떤 코드가 얼마나 자주 쓰이는지 알 수 없기에 Interpreter부터 시작하고 자주 실행되는 Hot Spot 코드를 발견 시 JIT Compiler가 바이트코드를 네이티브 기계어로 컴파일하고 결과를 메모리에 캐싱하는 것이다.

 

4) Garbage Collector(GC)

JVM은 힙 영역에 생성된 객체 중 더 이상 참조되지 않는 객체를 자동 제거한다.

GC 방식에는 Parallel GC, G1GC, ZGC 등이 있고 각 알고리즘마다 동작 방식이 조금 다르다.

Heap 사이즈, GC 알고리즘, GC 발생 빈도 등에 따라 적절하게 GC 튜닝이 필요할 수 있다.