[Java] JVM(Java Virtual Machine)의 구조와 동작방식

2025. 11. 7. 18:51·Backend/Java

1. JVM 이란?

Java 프로그램은 OS 위에서 직접 실행되지 않고 JVM(Java Virtual Machine) 위에서 실행됩니다.

오늘은 이 JVM의 구조와 동작 방식에 대해서 알아보겠습니다.


JVM은 바이트코드를 해석하고 실행하는 가상 머신입니다.

JVM은 운영체제와 무관하게 자바 프로그램을 실행할 수 있게 하는 중개자 역할을 하며 'Write Once, Run Anywhere(한 번 작성하면 모든 곳에서 실행된다)' 라는 자바의 핵심 특징을 가능하게 합니다.

자바 바이트코드(Java bytecode): Java 소스 코드를 컴파일하여 얻은, 자바 가상머신(JVM)이 이해할 수 있는 중간 코드




2. JVM의 구성 요소

 

JVM의 구성요소는 크게 클래스 로더(Class Loader), 실행 엔진(Execution Engine), 런타임 데이터 영역(Runtime Data Area)로 나눌 수 있습니다. 하나씩 살펴보겠습니다.



1) 클래스 로더(Class Loader), 동적 로딩

클래스 로더는 동적 로딩을 통해 필요한 클래스들을 가져와서 런타임 데이터 영역으로 올립니다.

여기서 동적 로딩이란 실제 실행할 때 필요한 시점에만 클래스 파일을 메모리에 올린다는 뜻 입니다.


동적 로딩은 Loading -> Linking -> Initialization 순서로 이루어집니다.

각 과정은 다음과 같습니다.

  • Loading (로드)
    • .class 파일을 찾아서 JVM 메모리의 Method Area에 올림
    • 클래스의 이름, 메서드, 필드, 접근 제어자 등 메타데이터를 저장
  • Linking (링크)
    • 로드된 클래스를 실제 사용할 수 있도록 검증 -> 준비 -> 해결 단계를 거침 
    • Verification (검증): 바이트 코드가 JVM 규칙에 맞는지 확인
    • Preparation (준비): static 변수에 메모리 공간 할당, 기본값으로 초기화
    • Resolution (해결): 클래스 이름 같은 심볼릭 참조를 실제 메모리 주소로 치환
  • Initialization (초기화)
    • static 변수에 실제 값을 넣고, static 블록 실행
    • 이 시점에 클래스가 실제로 사용 가능한 상태가 됨

static 변수: 클래스에 1개만 존재하는 변수로 클래스가 로딩될 때 생성

심볼릭 참조: 클래스, 메서드 필드 등을 이름(문자열)로 참조해 둔 상태




2) 실행 엔진 (Execution Engine)

실행 엔진(Execution Engine)은 클래스 로더가 메모리에 올려둔 바이트코드(.class)를 실제로 CPU가 이해할 수 있는 기계어로 변환하고 실행하는 역할을 합니다. 실행 엔진은 인터프리터, JIT 컴파일러, Garbage Collector 세부분으로 나눌 수 있습니다.

  • Interpreter
    • .class 파일의 바이트 코드를 한 줄씩 익고 바로 실행
    • 매번 해석해야 하므로 반복 실행되는 코드의 속도 저하
  • JIT Compiler
    • 자주 실행되는 코드를 감지하면 그 코드를 기계어(네이티브 코드)로 변환하여 캐싱(저장)
    • 캐싱된 코드는 해석 없이 바로 실행할 수 있기 떄문에 속도 향상 -> Interpreter의 단점 보완
  • Garbage Collector
    • Heap 영역의 메모리 관리 담당
    • 더 이상 참조되지 않는 객체를 찾아 메모리에서 제거
    • 개발자가 직접 메모리를 해제할 필요가 없어서 메모리 누수 위험을 낮춤

Garbage Collector는 다른 포스팅에서 좀 더 깊게 다뤄보겠습니다.



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

런타임 데이터 영역은 JVM이 프로그램을 실행하면서 실제로 데이터를 저장하고 관리하는 메모리 공간입니다.

자바 프로그램이 실행될 때 생성되는 모든 변수, 객체, 메서드 정보 등이 이 영역에서 관리됩니다.


런타임 데이터 영역은 크게 5가지로 나눌 수 있습니다.

  • Method Area 
    • 클래스 로더가 .class 파일을 읽어 들일 때 클래스 정보를 저장하는 공간
    • 클래스 이름, 메서드, 필드, 접근 제어자, static 변수, 상수 풀 등이 저장
    • JVM 전체에서 공유되는 영역
  • Heap 
    • 프로그램 실행 중 생성되는 모든 객체 인스턴스와 배열이 저장되는 공간
    • GC(Garbage Collector)가 관리하는 영역, 사용되지 않는 객체를 자동으로 제거
    • JVM에서 가장 큰 메모리 영역, 모든 스레드가 공유
  • Stack
    • 메서드가 호출될 때마다 생성되는 지역변수, 매개변수 임시값 등을 저장
    • 스레드 마다 독립적인 스택을 가짐
    • 메서드 끝나면 사라짐
  • PC Register
    • 현재 실행 중인 명령어의 주소(위치)를 저장
    • 스레드마다 독립적으로 존재하며, 다음 실행할 바이트 코드 명령의 주소를 보관
  • Native Method Stack
    • 네이티브 언어(실제 실행할 수 있는 기계어)된 메서드를 실행할 때 사용하는 스택
      ex) C, C++
    • 스택 구조

예시를 위해 간단한 코드를 보겠습니다.

class Person {
    static int population = 0;  
    String name;                

    public Person(String name) {
        this.name = name;
        population++;
    }

    public void sayHello() {   
        String greeting = "Hi, I'm " + name;
        System.out.println(greeting);
    }
}

public class Main {
    public static void main(String[] args) {
        Person p1 = new Person("건영"); 
        Person p2 = new Person("민수");  
        p1.sayHello();          
    }
}

 

  1. 프로그램 시작 시 Person 클래스가 로드
    1. Person.population(static 변수)가 Method Area에 만들어짐
    2. 값은 0
  2. p1 = new Person("건영") 실행 시
    1. new Person() 으로 Heap에 Person 객체 생성
    2. Heap 내부에 name = "건영" 저장
    3. p1은 Stack에 저장, Heap의 주소를 가리킴
    4. population++ 실행 -> Method Area의 값이 1이 됨
  3. p2 = new Person("민수") 실행 시
    1. 2번쨰 Person 객체 생성, Heap 내부에 name = "민수" 저장
    2. p2는 Stack에 저장, Heap의 두번째 객체 주소를 가리킴
    3. Method Area의  population 2로 증가
  4. p1.sayHello()
    1. sayHello() 메서드 호출 시 새로운 Stack 생성
    2. 지역변수 greeting이 stack에 저장, "Hi, I'm 건영" 출력
    3. 메서드 끝나면 해당 Stack 제거

여기서 기억할점은 p1 자체는 Stack에 있고, p1이 가리키는 객체(Person 인스턴스)는 Heap에 있다는 점 입니다.

즉 참조 변수(p1)은 Stack에 있고, 실제객체는 Heap에 있습니다.



3. JVM의 동작 방식

마지막으로 JVM이 어떻게 동작하는지 정리해보겠습니다.

  1. 컴파일 단계 (.java -> .class)
    1. 자바 컴파일러(javac)가 .java 파일을 바이트코드(.class)로 변환하는 단계 
  2. 클래스 로더 (Class Loader)
    1. 필요한 바이트코드(.class)를 JVM으로 가져오는 역할
    2. 동적 로딩을 통해 필요한 클래스들을 가져와서 Runtime Data Area의 Method Area, Heap에 적재
    3. Loading -> Linking -> Initialization 순서로 진행
  3. 실행 엔진 (Execution Engine)
    1. 로드된 바이트 코드를 CPU가 이해할 수 있는 기계어로 변환하고 실행
    2. Interpreter + JIT Compiler로 동작
    3. 실행 중 불필요한 객체는 Garbage Collector가 관리
  4. Runtime Data Area
    1. 자바 프로그램이 실행되는 동안 필요한 모든 데이터가 저장
    2. 메서드 종료 후 stack 에 저장된 변수 제거


정리하자면, JVM은 자바 프로그램을 실행하기 위한 가상 머신으로, 클래스 로더 → 실행 엔진 → 런타임 데이터 영역 → GC 의 과정을 통해
바이트코드를 실제로 실행하고 메모리를 효율적으로 관리합니다.

'Backend > Java' 카테고리의 다른 글

[Java] Annotation 어노테이션 알아보기, 커스텀 어노테이션 만들어보기  (1) 2025.04.26
'Backend/Java' 카테고리의 다른 글
  • [Java] Annotation 어노테이션 알아보기, 커스텀 어노테이션 만들어보기
단군왕건영
단군왕건영
널리 세상을 이롭게 하고 싶은 개발자
  • 단군왕건영
    홍익인간 개발자
    단군왕건영
  • 전체
    오늘
    어제
    • 분류 전체보기 (81) N
      • TroubleShooting (14)
      • Backend (11) N
        • Java (2)
        • Spring (7) N
        • JPA (2)
      • DB (1)
      • Algorithm (7)
        • 백준 (4)
      • Frontend (0)
        • React (0)
      • Infra (3)
      • CS (37)
        • 컴퓨터구조 (25)
        • 네트워크 (12)
      • Git (3)
      • Mac (2)
      • 회고 (1)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

    • GitHub
  • 공지사항

  • 인기 글

  • 태그

    MariaDB
    백준
    컴퓨터 구조
    java
    컴퓨터구조
    Jenkins
    docker
    spring
    springboot
    네트워크
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
단군왕건영
[Java] JVM(Java Virtual Machine)의 구조와 동작방식
상단으로

티스토리툴바