Post

[JAVA] JVM의 동작원리

[JAVA] JVM의 동작원리

JVM 구조도 JVM 요약도

JVM이란?

JVM(Java Virtual Machine)은 자바 바이트코드를 운영체제에 독립적으로 실행할 수 있도록 해주는 가상 실행 환경으로, 클래스 로딩부터 메모리 관리, 실행까지 전 과정을 담당하는 자바의 핵심 엔진입니다.

  • 사용자가 작성한 코드는 Javac(자바 컴파일러)를 통해 JVM이 이해할 수 있는 바이트코드로 변환됩니다.
  • 변환된 바이트코드는 .class 파일로 저장되며, 운영체제에 관계없이 JVM에서 실행할 수 있습니다.
  • 하지만 바이트코드는 기계어가 아니기 때문에, JVM이 실행 시 운영체제에 맞는 기계어로 변환하여 실행합니다.

JVM 구조도 JVM의 전체 구조와 흐름도

JVM의 구성요소

  • 클래스 로더 (Class Loader)
  • 실행 엔진 (Execution Engine)
  • 런타임 데이터 영역 (Runtime Data Area)
  • JNI (Java Native Interface)
  • 네이티브 메서드 라이브러리 (Native Method Library)

클래스 로더 (Class Loader)

JVM은 .class 파일을 읽어 바이트코드를 JVM 메모리(Method Area)에 로드합니다.
이때 모든 클래스를 한 번에 올리는 것이 아니라, 애플리케이션이 필요로 하는 시점에 동적으로 할당합니다.

Class Loading Flow 클래스 로딩과 Linking, Initialization 과정

클래스 로딩 과정

  1. Loading (로드): .class 파일을 JVM 메모리로 로드
  2. Linking (링크): JVM에서 실행할 수 있도록 준비하는 단계
    • Verifying (검증): .class 파일이 JVM 명세에 맞게 구성되었는지 확인
    • Preparing (준비): static 변수 등을 위한 메모리 공간 할당 및 기본값 설정
    • Resolving (분석): 모든 심볼릭 레퍼런스다이렉트 레퍼런스로 변환
  3. Initialization (초기화): static 변수에 실제 값 할당 등 초기화 수행

심볼릭 vs 다이렉트 레퍼런스

  • 심볼릭 레퍼런스: “어디 있는지 모른 채 이름만 알고 있는 상태” (예: 변수명, 클래스명)
  • 다이렉트 레퍼런스: JVM이 메모리 주소를 확인하고 저장 → 즉시 접근 가능
  • 처음엔 “집앞 카페”라고만 알고 있다가, 한 번 가본 후엔 “정확한 주소”로 빠르게 찾아가는 것과 같음

실행 엔진 (Execution Engine)

JVM은 바이트코드를 실제 기계어로 변환하여 실행하기 위해 인터프리터(Interpreter)JIT(Just-In-Time) 컴파일러 방식을 함께 사용합니다.

Execution Engine 구조 JVM 실행 엔진의 구조

실행 방식

  1. 인터프리터: 바이트코드를 한 줄씩 해석하여 실행
    (초기 실행 속도는 빠르지만, 반복 실행 시 느림)
  2. JIT 컴파일러: 자주 실행되는 코드를 네이티브 코드로 변환하여 캐싱 → 속도 최적화

JIT의 최적화 기법

  • 인라이닝: 자주 호출되는 메서드를 직접 삽입해 호출 오버헤드 제거
  • 루프 언롤링: 반복문을 펼쳐 반복 횟수를 줄이고 분기 비용 감소
  • 동적 디스패치 제거: 메서드 호출을 정적으로 고정해 불필요한 동적 호출 제거

실행 흐름

바이트코드 → 인터프리터 실행 → JIT 컴파일러가 반복 코드 최적화 → 네이티브 코드로 변환 → 실행 속도 향상

가비지 컬렉션 (GC)

JVM은 사용하지 않는 객체를 자동으로 제거하여 메모리를 관리합니다.

GC 주요 개념

  • Young Generation / Old Generation
  • Minor GC: Young Gen에서 수행, 빠름
  • Major GC: Old Gen에서 수행, 느림
  • Full GC: 전체 Heap 대상, Stop-The-World 발생 가능

GC 튜닝

  • -Xms: 초기 힙 크기
  • -Xmx: 최대 힙 크기
  • -XX:+UseG1GC: G1 GC 사용
  • -XX:+PrintGCDetails: GC 상세 로그 출력

GC는 JVM 성능의 핵심 요소이며, 최근에는 멈추지 않는 초저지연 GC가 트렌드입니다.

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

JVM은 실행 중 다양한 메모리 영역을 활용합니다.

메모리 구조 JVM 메모리 구조

메서드 영역

  • 클래스 메타정보, static 변수, 런타임 상수 풀 등 저장
  • Java 8부터는 Metaspace 사용
  • 과도하게 사용되면 OutOfMemoryError 발생

런타임 상수 풀 예시

1
2
3
String a = "Java";
String b = "Java";
System.out.println(a == b); // true
1
2
3
4
String a = new String("Java");
String b = "Java";
System.out.println(a == b);       // false
System.out.println(a.equals(b)); // true
1
2
3
4
String a = new String("Java");
String b = a.intern();
String c = "Java";
System.out.println(b == c); // true

문자열 메모리 구조

힙 영역

Heap

  • 모든 객체와 배열 저장
  • GC가 대상 탐지 및 제거
  • Young Gen: Eden + Survivor 0/1, Minor GC
  • Old Gen: 장기 객체 저장, Major GC

스택 영역

JVM 스택 구조

  • 스레드별 독립적, LIFO 구조
  • 메서드 호출 시 프레임 생성/제거
1
String a = new String("Java"); // Heap에 객체, Stack에 참조 변수

Stack과 힙 예시

PC 레지스터

  • 현재 실행 중인 명령어의 주소 저장
  • 스레드마다 존재
1
2
System.out.println("Hi");
// getstatic → ldc → invokevirtual 순으로 PC 레지스터가 위치 추적

네이티브 메서드 스택

  • C, C++ 등의 네이티브 메서드 호출 시 사용

JNI (Java Native Interface)

  • 자바 ↔ 네이티브 간의 브릿지 역할

복잡성, 디버깅 난이도, 충돌 가능성 주의


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

1
2
3
4
5
6
7
8
9
class Test {
    static int staticVar = 10;         // 메서드 영역에 저장
    int instanceVar = 5;               // Heap에 저장

    void greet(String name) {         // 메서드 정의는 메서드 영역
        String msg = "Hello " + name; // 지역 변수는 Stack에 저장
        System.out.println(msg);      // 메서드 호출 → 스택 프레임 생성
    }
}
  • JVM이 Test 클래스를 로딩
    staticVar, greet() 메서드 바이트코드 등은 메서드 영역(Method Area) 에 저장됨
    static이기 때문에 메서드 영역에 저장

  • new Test() 객체 생성
    instanceVar는 인스턴스 변수로, 객체와 함께 Heap 영역에 저장됨
    객체가 생성될 때마다 새로 생기므로 Heap에 저장

  • hello.greet("JVM") 호출
    "JVM" (매개변수)와 "Hello JVM" (지역 변수 msg)는 Stack 영역에 저장됨
    System.out.println()도 호출되며 스택 프레임(Stack Frame) 이 하나 더 생성됨

  • 메서드 실행 종료
    greet()println()스택 프레임은 제거
    Heap에 남은 hello 객체는 더 이상 참조되지 않으면 GC 대상이 될 수 있음


✅ 기억해야 할 것

JVM 명세에 따르면 Method Area는 논리적으로 힙의 일부로 간주되기도 하지만, 클래스 정보와 실행 코드, 상수 풀 등을 저장하며 일반적인 객체를 저장하는 힙과는 기능적으로 명확히 다르다. 이러한 차이로 인해 JVM 메모리 구조도에서는 보통 Method Area를 힙과 분리된 영역으로 시각화한다.
🔗 JVM Spec §2.5.4 - Method Area

변수 종류 저장 위치 설명
static 변수 메서드 영역 클래스 로딩 시 1번만 생성, 인스턴스 공유
인스턴스 변수 Heap 영역 객체 생성 시마다 생성
지역 변수 Stack 영역 메서드 호출 시 생성, 종료 시 제거
This post is licensed under CC BY 4.0 by the author.

Trending Tags