JVM(Java Virtual Machine) 완벽 정리 🚀
1. JVM이란?
JVM(Java Virtual Machine)은 자바 애플리케이션을 실행하기 위한 가상 머신입니다.
개발자가 작성한 자바 코드는 바이트코드(Bytecode, .class 파일)로 변환된 후, JVM이 이를 실행합니다.
JVM의 가장 큰 장점 중 하나는 운영체제(OS)에 종속되지 않는다는 것입니다.
이는 자바의 “Write Once, Run Anywhere” 원칙을 가능하게 합니다.
💡 즉, 자바 프로그램이 어떤 운영체제에서든 실행될 수 있도록 해주는 역할을 합니다.

2. JVM의 주요 기능
① 바이트코드 실행
• 자바 컴파일러(javac)가 .java 파일을 .class 바이트코드 파일로 변환.
• 클래스 로더에 의해 전달된 바이트코드를 JVM이 읽고 실행.
② 메모리 관리 (Garbage Collection)
• 객체를 생성하고 메모리를 자동으로 해제하는 GC(Garbage Collection) 수행.
• C계열 언어와 달리 개발자가 수동으로 메모리 해제할 필요가 없음.
③ 플랫폼 독립성 제공
• OS에 따라 다른 JVM이 제공되지만, 같은 바이트코드를 실행 가능.
• 예) Windows용 JVM, Linux용 JVM, Mac용 JVM이 다름.
- 운영체제(OS)마다 제공하는 JVM 바이너리가 다르기 때문
- 예를 들어, OpenJDK 17을 다운로드할 때 다음과 같은 OS별 파일이 제공됨.
• Linux: openjdk-17_linux-.tar.gz
• Mac: openjdk-17_macos-x64_bin.tar.gz
• Windows: openjdk-17_windows-x64_bin.zip - JIT 컴파일러는 OS와 CPU 아키텍처에 따라 서로 다른 네이티브 코드(machine code)를 생성.
- 즉, 같은 System.out.println("Hello World") 코드라도 Windows에서 실행되는 네이티브 코드와 Linux에서 실행되는 네이티브 코드가 다를 수 있음.
- JVM은 OS의 시스템 호출(System Calls)을 통해 OS와 상호작용(파일 입출력(I/O), 스레드 관리, 네트워크 통신 등)
즉, OS의 API를 이용해 시스템 리소스에 접근. - 각 운영체제마다 제공하는 시스템 API가 다르기 때문에, JVM이 OS에 맞춰야 함.
- Windows에서는 CreateFile() API 사용
- Linux에서는 open() API 사용
// Window HANDLE hFile = CreateFile( "test.txt", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); //Linux int fd = open("test.txt", O_RDONLY);
④ JIT(Just-In-Time) 컴파일을 통한 최적화
• 기본적으로 인터프리터 방식으로 실행.
• 반복 실행되는 코드는 JIT 컴파일러가 네이티브 코드로 변환하여 성능 향상.
3. JVM의 구성 요소
JVM은 크게 클래스 로더(Class Loader), 런타임 메모리 영역(Runtime Data Area), 실행 엔진(Execution Engine) 으로 구성됩니다.

① Class Loader (클래스 로더)
• .class 파일(바이트코드)을 JVM으로 로드하는 역할.
• 로딩된 클래스들은 Method Area(메서드 영역)에 저장됨.
• 클래스 로딩 과정
1. 로딩(Loading) → .class 파일을 찾아서 JVM에 로드.
2. 링크(Linking) → 검증(Verify), 준비(Prepare), 해석(Resolve) 과정 수행.
3. 초기화(Initialization) → 클래스 변수(static 변수) 초기화 수행.
보다 자세한 설명은 아래 링크 참고 부탁드립니다.
자바 컴파일 과정
자바(Java)의 컴파일 과정은 크게 4단계로 진행됩니다. 먼저 짚고 넘어가야할 것은, 자바는 JVM을 통해 OS에 독립적인 특징을 갖고 있다는 것입니다. 이는 즉 자바 코드가 컴파일되면 바로 CPU에서
hyem5019.tistory.com
💡 클래스 로더는 “Lazy Loading” 기법을 사용하여 필요한 순간까지 클래스를 로드하지 않음.
- 이는 메모리 최적화를 도모하기 위함.
만약 프로그램 시작 시 모든 클래스를 한 번에 로드한다면, 힙(Heap) 영역이 불필요하게 커지면서 메모리 낭비가 발생함. - 이는 애플리케이션 속도 향상까지 연계됨.(모든 클래스를 한 번에 로드하면 JVM 시작 시간이 길어짐)
- 또한 GC의 효율 향상까지 이어짐.
② Runtime Data Area (런타임 데이터 영역, JVM 메모리 구조)
JVM은 여러 개의 메모리 영역을 관리하며, 크게 Heap, Stack, Method Area, PC Register, Native Method Stack으로 구분.
📌 JVM 메모리 구조

💡 Heap 영역과 Stack 영역은 중요한 메모리 관리 대상이며, Heap 영역은 GC(Garbage Collector)의 영향을 받음.
아래 예시 코드의 구성 요소가 각각 runtime data area 어느 위치에 저장되는지 확인.
public class Person { // 멤버 변수 (필드) -> 인스턴스 변수 private String name;// 인스턴스 변수 (Heap에 저장) private int age;// 인스턴스 변수 (Heap에 저장) // 정적 변수 (클래스 변수) private static String species = "Homo sapiens"; // 생성자 public Person(String name, int age) { // name과 age는 지역 변수 (Stack에 저장) this.name = name; // Heap에 저장된 객체의 필드(name)에 값 저장 this.age = age; // Heap에 저장된 객체의 필드(age)에 값 저장 } // 인스턴스 메서드 public void sayHello() { // sayHello() 메서드는 Method Area에 저장 System.out.println("Hello, my name is " + name); } // 정적 메서드 public static void printSpecies() { System.out.println("We are " + species); } }
Method 영역(모든 쓰레드가 공유)
- 정적 변수 & String Constant Pool : private static String species = "Homo sapiens";
- 생성자(메서드 일종) : public Person(String name, int age) {...}
- 인스턴스 메서드 : public void sayHello() {...}
- 정적 메서드 : public static void printSpecies() {...}
- 정적 필드와 클래스 구조, 클래스의 메타데이터 저장.
- 클래스타입,상수,메서드 이름,리턴 타입,메서드 매개변수, 접근 제어자, Static 변수 보관.
- JVM 시작시 생성되어 프로그램 종료까지 존재.
- 명시적으로 null값을 선언해야 GC대상이 됨.
Heap 영역(모든 쓰레드가 공유)
- 인스턴스 변수 : private String name;
- 인스턴스 변수 : private int age;
- 인스턴스 : Person p1 = new Person("Alice",30);
- 런타임 시 동적으로 할당하여 사용하는 메모리 영역.
- 객체,인스턴스와 배열 저장.
- 더이상 객체가 쓰일 일이 없다고 판단되면 GC 대상.
Stack 영역(각 쓰레드별 생성)
- 생성자 지역변수(메서드 실행 중에만 일시적으로 저장) : this.name = name; this.age = age;
- 인스턴스 변수명 : Person p1 = new Person("Alice",30);
- FILO 구조.
- 힙 영역의 인스턴스 참조 값 저장.

PC레지스터(각 쓰레드별 생성)
- 현재 실행 중인 JVM 주소 저장.
Natice Method Stack Area(각 쓰레드별 생성)
- 자바 외 언어로 작성된 네이티브 코드를 위한 메모리.
- JIT 컴파일러에 의해 변환된 네이티브 코드가 여기서 실행.
③ Execution Engine (실행 엔진)
• JVM의 핵심 역할을 담당하는 엔진.
• 바이트코드를 해석하여 실행하는 역할.
• 두 가지 방식이 존재.
📌 1) 인터프리터 (Interpreter)
• 바이트코드를 한 줄씩 해석하여 실행.
• 실행 속도가 느리지만, 즉시 실행이 가능.
📌 2) JIT(Just-In-Time) 컴파일러
• 자주 실행되는 코드를 네이티브 코드(기계어)로 변환하여 캐싱 후 실행.
• 실행 속도를 향상시키는 역할.
💡 JIT 컴파일러 덕분에 자바는 컴파일 언어(C, C++)보다 실행 속도가 느리지만, 반복 실행 시 성능이 크게 향상됨.
4. JVM의 실행 과정 (자바 코드 실행 흐름)
자바 프로그램이 실행되는 과정은 다음과 같습니다.
1️⃣ 자바 소스 코드 작성 (.java 파일)
2️⃣ 컴파일 (javac) → 바이트코드 (.class 파일) 생성
3️⃣ JVM이 Class Loader를 통해 .class 파일 로드
4️⃣ 메서드 영역(Method Area)에 클래스 정보 저장
5️⃣ Heap 영역에 객체 생성 및 Stack에서 메서드 실행
6️⃣ Execution Engine이 바이트코드를 해석하여 실행 (인터프리터 & JIT 컴파일러 작동)
7️⃣ Garbage Collector가 사용되지 않는 객체 정리
5. Garbage Collection (GC, 가비지 컬렉션)
• JVM이 Heap 영역의 사용되지 않는 객체를 자동으로 제거하여 메모리를 관리.
• GC는 주기적으로 실행되며, 객체의 생존 여부를 판단하여 메모리를 해제.
📌 GC 동작 방식
• Mark & Sweep 알고리즘 사용.
1. Mark 단계: 사용 중인 객체와 미사용 객체를 구분.
2. Sweep 단계: 미사용 객체를 삭제하여 메모리를 확보.
💡 GC는 자동으로 수행되지만, 최적화를 위해 G1 GC, ZGC, Parallel GC 등의 전략을 선택할 수 있음.
'Development > Java' 카테고리의 다른 글
자바 컴파일 과정 (6) | 2025.03.16 |
---|---|
자바는 왜 객체지향 언어인가요? (4) | 2025.03.15 |
객체지향 설계원칙 : SOLID 란? (3) | 2024.02.27 |
객체 지향 프로그래밍이란?? 객체 지향의 4가지 특징 Feat.캡상추다 (2) | 2024.02.26 |
boolean 바인딩 에러.. boolean과 Boolean의 차이 (2) | 2024.02.02 |