자바 프로그램의 실행과정
보통 다음과 같은 간단한 프로그램을 작성하고 실행한다.
public class Start {
public static void main(String[] args) {
System.out.println("Hello OOP!!");
}
}
코드는 단 한 줄을 출력하지만, 실제로는 이 한 줄이 실행되기까지 JRE와 JVM이 뒤에서 상당히 많은 일을 처리한다.
1. 프로그램 실행 버튼을 누르면 가장 먼저 JRE가 움직인다.
자바 프로그램을 실행하면 가장 먼저 JRE(Java Runtime Environment)가 실행된다.
JRE의 역할은 쉽게 말해 자바 프로그램을 실행하기 위한 관리자라고 생각하면 된다.
JRE는 먼저 프로그램 안에 main() 메서드가 존재하는지 확인한다.
public static void main(String[] args)
자바 프로그램은 반드시 main() 메서드에서 시작하기 때문이다.
만약 main() 메서드가 없다면 JRE는 프로그램을 어디서부터 실행해야 할지 알 수 없으므로 실행 자체가 불가능하다.
2. JRE는 JVM을 부팅한다.
main() 메서드를 찾았다면, JRE는 실제 실행을 담당하는 JVM(Java Virtual Machine)을 실행시킨다.
쉽게 표현하면 다음과 같다.
개발자
↓
JRE (실행 관리자)
↓
JVM (실제 실행 엔진)
자동차에 비유하면,
- JRE : 운전자를 태우고 시동을 거는 사람
- JVM : 실제로 달리는 엔진
이라고 생각하면 이해하기 쉽다.
3. JVM은 프로그램 실행을 위한 메모리를 준비한다.
JVM이 실행되면 가장 먼저 메모리 구조를 만든다.
Method Area (Static 영역)
Heap 영역
Stack 영역
Method Area (Static 영역)
클래스 정보, static 변수, static 메서드 등이 저장되는 공간이다.
Heap 영역
객체(new)가 생성되는 공간이다.
Stack 영역
메서드가 실행될 때 필요한 지역 변수와 매개변수가 저장되는 공간이다.
4. java.lang 패키지를 먼저 메모리에 올린다.
모든 자바 프로그램은 자동으로 java.lang 패키지를 포함한다.
다음과 같은 코드를 작성할 수 있는 이유도 여기에 있다.
System.out.println("Hello");
String str = "Java";
Math.random();
사용자가 한 번도
import java.lang.System;
import java.lang.String;
import java.lang.Math;
를 작성한 적이 없다.
그런데도 사용할 수 있는 이유는 JVM이 가장 먼저 java.lang 패키지를 메모리에 올려 주기 때문이다.
즉,
java.lang
├── System
├── String
├── Math
├── Object
└── Exception
등의 클래스가 미리 준비되는 것이다.
5. import된 패키지와 클래스들을 메모리에 올린다.
예를 들어,
import java.util.Scanner;
를 작성했다면 JVM은 Scanner 클래스 정보도 메모리에 로딩한다.
또한 프로그램에서 사용하는 클래스들도 함께 메모리에 올라간다.
Method Area
├── java.lang
├── Scanner
└── Start 클래스
이처럼 JVM은 프로그램 실행 전에 필요한 클래스 정보를 미리 준비한다.
6. main() 메서드가 실행될 준비를 한다.
여기까지 오면 바로
System.out.println("Hello OOP!!");
가 실행된다고 생각할 수 있지만 아직 아니다.
메서드는 Stack 영역에서 실행된다.
JVM은 먼저 main() 메서드를 위한 공간을 Stack에 생성한다.
이 공간을 스택 프레임(Stack Frame) 이라고 한다.
Stack
┌─────────────────┐
│ main() Frame │
└─────────────────┘
스택 프레임은 하나의 메서드가 실행되는 동안 필요한 모든 정보를 저장하는 작업 공간이다.
7. 매개변수 공간을 확보한다.
main() 메서드에는 다음과 같은 매개변수가 있다.
String[] args
따라서 JVM은 스택 프레임 안에 args 변수를 저장할 공간도 만든다.
Stack
┌─────────────────┐
│ main() Frame │
│ ─────────────── │
│ args │
└─────────────────┘
비록 명령행 인자를 사용하지 않더라도, args 변수 공간 자체는 반드시 생성된다.
8. 이제 첫 번째 문장이 실행된다.
System.out.println("Hello OOP!!");
가 실행된다.
실행 과정은 다음과 같다.
JRE 실행
↓
main() 메서드 확인
↓
JVM 부팅
↓
메모리 영역 생성
↓
java.lang 로딩
↓
필요한 클래스 로딩
↓
main() 스택 프레임 생성
↓
args 공간 생성
↓
첫 번째 문장 실행
정리
작성한 프로그램은 단 한 줄을 출력하지만, 실제로는 그 뒤에서 JRE와 JVM이 상당히 많은 일을 처리한다.
특히 기억해야 할 것은 다음 세 가지다.
- JRE는 main() 메서드를 찾아 JVM을 실행한다.
- JVM은 메모리 구조를 만들고 필요한 클래스들을 메모리에 로딩한다.
- main() 메서드의 스택 프레임과 매개변수 공간까지 준비된 후에야 첫 번째 코드가 실행된다.
즉, 우리가 보는 System.out.println("Hello OOP!!"); 한 줄 뒤에는 생각보다 복잡한 준비 과정이 숨어 있는 것이다.
출처 : [스프링 입문을 위한 자바 객제 지향의 원리와 이해]
'TIL' 카테고리의 다른 글
| 자바 instanceof 쉽게 이해하기 : 타입 검사보다 중요한 진짜 사용 목적 (0) | 2026.06.19 |
|---|---|
| 자바 Class 객체란? 리플렉션(Reflection) 개념과 사용 이유 (0) | 2026.06.18 |
| 객체와 메모리, 메서드 개념 확인 (0) | 2026.06.16 |
| 왜 equals()와 hashCode()를 함께 재정의해야 할까? (0) | 2026.06.15 |
| [TIL] 객체지향 설계가 어려운 이유: 문법이 아니라 도메인 이해의 문제 (0) | 2026.06.09 |