Java Virtual Machine

Jerry·2025년 7월 23일

Topic

JVM

What I Learned

JVM Architecture

Class Loader (클래스 로더)

  • 역할: .class 파일(바이트코드)을 JVM 메모리에 로딩하는 컴포넌트
  • 주요 기능
    • Loading: 클래스 파일을 찾아 메모리로 적재
    • Linking:
      • Verification: 바이트코드가 JVM 규격에 맞는지 검증
      • Preparation: static 멤버 변수에 기본값 할당
      • Resolution: 심볼릭 레퍼런스를 실제 메모리 주소로 치환
    • Initialization: static 블록/변수 초기화
  • 클래스 로더 계층
    • Bootstrap ClassLoader (JRE 기본 클래스 로딩)
    • Extension ClassLoader (JRE 확장 디렉토리)
    • Application ClassLoader (사용자 클래스)

JVM Memory

Method Area

  • 용도:
    • 클래스 구조(메타데이터), static 변수, 상수 풀, 메서드 정보, 인터페이스 등 저장
  • 특징:
    • JVM 당 1개 존재(스레드 공유)
    • PermGen(8 이전) → Metaspace(8 이후)
  • GC 대상: 일부 메타데이터 가능

Heap

  • 용도:
    • new로 생성되는 객체 인스턴스, 배열 등을 저장
  • 특징:
    • JVM 당 1개 존재(스레드 공유)
    • GC의 주요 타겟
    • Young/Old 영역으로 나뉨 (Generational GC)
  • 실제 애플리케이션 데이터(객체)가 존재

JVM Language Stacks (Java 스택)

  • 용도:
    • 각 스레드마다 생성되는 스택 프레임(메서드 호출 시 지역 변수, 피연산자, 리턴 주소 등)
  • 특징:
    • 스레드별 독립적(각 스레드마다 하나씩)
    • 스택 오버플로우 발생 가능
    • 지역변수/프리미티브/참조형 변수 저장

PC Registers (프로그램 카운터 레지스터)

  • 용도:
    • 현재 스레드가 실행 중인 JVM 명령(바이트코드)의 주소를 저장
  • 특징:
    • 스레드별로 별도 존재
    • 네이티브 메서드 수행 시 undefined

Native Method Stacks (네이티브 메서드 스택)

  • 용도:
    • C, C++ 등 JVM 이외의 네이티브 코드를 실행할 때 사용되는 스택
  • 특징:
    • JNI(Java Native Interface) 기반
    • 플랫폼별로 구현 차이 있음

Execution Engine (실행 엔진)

  • 설명:
    • JVM 메모리에 적재된 바이트 코드를 실제 머신 코드로 해석/실행하는 컴포넌트
  • 구성
    • 인터프리터(Interpreter): 바이트코드를 한 줄씩 해석하여 실행(속도 느림)
    • JIT 컴파일러(Just-In-Time Compiler): 실행 중 반복적으로 호출되는 메서드를 네이티브 코드로 변환하여 속도 향상
      • Code Cache에 저장

Native Method Interface

  • 설명:
    • 자바 바이트코드와 C/C++ 등 네이티브 라이브러리 간의 인터페이스(JNI)
    • 자바에서 네이티브 메서드를 호출할 때 사용(native 키워드)
  • 의의:
    • OS, 하드웨어 접근 등 자바로 할 수 없는 영역 지원

Native Method Libraries (Shared library)

  • 설명:
    • 실제 네이티브 코드(.dll, .so 등)
    • 예시: OpenGL, 파일 시스템, 하드웨어 제어 등

요약

  1. 클래스 로더가 클래스 파일을 JVM 메모리에 로딩
  2. Method Area에 클래스 구조, Heap에 객체, Stack에 메서드 실행 환경 저장
  3. Execution Engine이 바이트코드를 해석(JIT 포함)하여 실행
  4. 바이트코드에서 네이티브 코드가 필요하면 Native Method Interface를 통해 Native Libraries 호출
컴포넌트설명스레드 공유 여부
Class Loader.class 로딩 및 메타데이터 관리, 클래스 로더 계층 존재공유
Method Area클래스 구조, static 변수, 상수 풀 등 저장공유
Heap객체 인스턴스 저장(GC 대상)공유
JVM Language Stacks메서드 호출 시마다 스택 프레임 생성(지역변수, 피연산자 등)비공유
PC Registers현재 실행 중인 명령 주소 저장비공유
Native Method Stacks네이티브 코드 실행 위한 스택비공유
Execution Engine바이트코드 해석/실행(JIT 포함)-
Native Method IFJNI, 자바↔네이티브 코드 브릿지-
Native Libraries실제 네이티브 코드(.dll, .so 등)-

예시

코드

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

컴파일 및 실행 흐름

(1) 소스코드 작성

Hello.java → 개발자가 자바 소스코드 작성

(2) 컴파일

javac Hello.java

자바 컴파일러(javac)가 .java 파일을 바이트코드(.class)로 변환

(3) 클래스 로더

java Hello 실행 시, JVM의 Class Loader가 Hello.class 파일을 메모리로 로딩

JVM 메모리 구조에 데이터가 저장되는 흐름

1. Method Area

  • Hello 클래스의 구조(메타데이터, static 정보, 메서드 시그니처 등)
  • main 메서드의 바이트코드, 상수 풀 등

2. Heap

  • "Hello, JVM!" 문자열 객체
  • args 배열 객체
  • 기타 new로 생성된 객체

3. Java Stack (JVM Language Stacks)

  • main 메서드 실행 시 스택 프레임
    • 지역 변수(args)
    • 연산 중 필요한 임시 값

4. PC Register

  • 현재 실행중인 JVM 명령의 위치(메서드 내 바이트코드 오프셋)

5. Native Method Stack

  • (여기서는 사용되지 않음. 예: C 라이브러리 호출할 때 사용)

전체 흐름 순서도

소스코드(Hello.java)
   │
   └──> [javac] 컴파일
               │
               └──> Hello.class (바이트코드)
                               │
                               └──> [java Hello] 실행
                                            │
                                            ├─> [Class Loader] Method Area로 로딩
                                            │      └─> 클래스 정보, 메서드 바이트코드 저장
                                            ├─> [Heap] 문자열, 배열 등 객체 생성
                                            ├─> [Stack] main 메서드 실행 스택 프레임
                                            └─> [Execution Engine] 바이트코드 해석 실행

Reference

profile
Backend engineer

0개의 댓글