Skip to main content

Command Palette

Search for a command to run...

Java 프로그램이 실행되는 흐름

Published
2 min read

자바 프로그램은 단순히 .java 파일을 실행하는 것이 아니라, 컴파일 → 로드 → 실행 이라는 여러 단계를 거쳐 최종적으로 CPU 가 이해할 수 있는 기계어로 변환됩니다.

이 과정에서 JDK, JVM, JRE가 각각 어떤 역할을 하는지 이해하는 것이 중요합니다.


1. 소스 코드 작성과 컴파일

  • 개발자는 자바 소스 파일(.java)을 작성

  • JDK 에 포함된 javac (Java Compiler)가 소스 코드를 컴파일 하여 JVM이 이해할 수 있는 바이트코드(.class 파일)로 변환

  • 이 바이트 코드는 특정 운영체제에 종속되지 않으며, “Write Once, Run Anywhere”를 가능하게 하는 핵심

  • 즉 .java → .class 변환 과정이 여기서 일어남


2. 클래스 로딩(Class Loading)

프로그램 실행 시 , JVM 의 클래스 로더(Class Loader)가 .class 파일을 읽어 JVM 메모리(Runtime Data Areas)에 올립니다.

클래스 로딩은 정적인 방식이 아닌 동적 로딩을 사용합니다.

동적 로딩이란?

프로그램 시작 시 모든 클래스를 한꺼번에 메모리에 적재하지 않고, 실제로 필요한 순간 (객체 생성, static 변수/ 메서드 참조 시)에만 메모리에 로딩하는 방식.

불필요한 클래스가 메모리에 올라가지 않아, 메모리 효율성이 좋아지고 프로그램 실행 속도도 빨라짐


3. 로딩(Loading) → 링킹(Linking) → 초기화(Initialization)

클래스 로더가 .class 파일을 JVM 메모리에 로드하면, 다음과 같은 단계를 거칩니다.

  1. Loading

    • .class파일의 바이트 코드를 읽어, Method Area(메서드 영역) 에 저장
  2. Linking

    • 클래스가 실행 가능한 상태가 되도록 준비하는 과정

      • Verification(검증) : 클래스 파일이 JVM 명세에 맞게 작성되었는지 확인 → 보안성 확보

      • Preparation(준비) : static 변수를 위한 메모리 공간을 확보하고 기본값으로 초기화

      • Resolution(해결) : Constant Pool 에 있는 심볼릭 레퍼런스를 실제 메모리 주소로 변환

  3. Initialization

    • static 변수에 사용자가 지정한 값 할당

    • static 블록 실행

이 단계들이 끝나야 클래스가 실행될 준비를 마치게 됩니다.


4. 실행 엔진(Execution Engine)

로드 및 초기화된 클래스의 바이트 코드는 최종적으로 실행 엔진(Execution Engine)에 의해 실행됩니다.

하지만 CPU는 바이트코드를 직접 이해할 수 없기 때문에 기계어 변환 과정이 필요합니다.

JVM은 두가지 방식을 함께 사용합니다.

  • 인터프리터 (Interpreter)

    • 바이트코드를 한줄씩 해석하고 실행

    • 빠른 시작

    • 같은 코드가 반복될 때마다 매번 해석 → 느림

  • JIT 컴파일러 (Just-In-Time Compiler)

    • 실행 중에 자주 호출되는 메서드를 네이티브 코드로 변환하고 캐싱

    • 한 번 변환되면 이후에는 기계어 그대로 실행 → 빠름

    • 컴파일 과정 자체에 시간이 걸림 (초기 오버헤드 발생)

→ JVM은 처음에는 인터프리터로 실행하고 반복되는 부분은 JIT로 변환하는 하이브리드 방식을 사용하며, 빠른 시작과 높은 성능을 동시에 얻습니다.


5. 실행 환경(JRE와 JVM)

  • JRE (Java Runtime Environment)

    • 자바 실행에 필요한 기본 라이브러리 + JVM 으로 구성

    • 컴파일된 프로그램을 실행할 수 있는 환경을 제공

  • JVM (Java Virtual Machine)

    • 바이트코드를 실행하는 가상머신

    • 운영체제 위에서 동작하면서, OS와 프로그램 사이에서 중개자 역할 수행

More from this blog

낙관적 락(Optimistic Lock)과 비관적 락(Pessimistic Lock)

락(Lock)의 필요성 현대 애플리케이션은 대부분 동시성(Concurrency) 문제에 직면합니다. 여러 사용자나 프로세스가 동시에 같은 데이터를 수정하거나, 은행 계좌 이체와 같은 중요한 트랙잭션을 동시에 실행하는 경우가 있습니다. 이때 동시 접근을 적절히 제어하지 않으면, 데이터 불일치, 중복 처리, 시스템 오류까지 발생할 수 있습니다. 이를 방지하기 위해 락(Lock) 개념이 도입되었습니다. 락은 여러 프로세스나 스레드가 동시에 동일한 자...

Aug 22, 20253 min read

Java Collection Framework

컬렉션 프레임워크(Collection Framework)란? 자바에서 컬렉션 프레임워크란 다수의 데이터를 쉽고 효과적으로 처리할 수 있는 표준화된 방법을 제공하는 클래스와 인터페이스 집합입니다. 즉, 데이터를 저장하는 자료구조과 데이터를 처리하는 알고리즘을 구조화하여 클래스로 구현한 것입니다. 모든 컬렉션 프레임워크는 자바의 인터페이스를 기반으로 구현됩니다. JFC(Java Collection Framework)의 도입 배경 JCF 이전에는 ...

Aug 14, 20254 min read

운영체제의 발전: 단일 프로세스에서 멀티코어까지

운영체제의 발전은 ‘CPU를 얼마나 효율적으로 활용하느냐’의 역사라고 볼 수 있습니다. 초기에는 한 번에 하나의 프로그램만 실행했지만, 점차 CPU 사용률과 사용자 경험을 높이기 위해 멀티프로그래밍, 멀티태스킹, 멀티프로세스, 멀티스레딩, 그리고 멀티코어 구조가 발전했습니다. 들어가기에 앞서 프로세스와 스레드에 대해 간단히 설명하겠습니다. 프로세스(Process) 실행 중인 프로그램으로 OS로부터 독립된 주소 공간과 자원을 할당 받음 각 ...

Aug 13, 20252 min read

Keep-Alive (HTTP Keep-Alive와 TCP Keep-Alive)

Keep-Alive는 연결(Connection)을 계속 유지하기 위한 메커니즘입니다. 네트워크에서 어떤 연결을 만들고, 요청/응답을 주고받은 후 바로 끊지 않고 일정 시간 동안 유지하면, 그 시간 안에 들어오는 추가 요청은 이미 열린 연결을 재사용할 수 있습니다. 이를 통해 매번 연결을 새로 맺는데 필요한 오버헤드(3-Way Handshake)를 줄일 수 있고, 불필요한 지연(latency)를 감소시킬 수 있습니다. 하지만 연결을 너무 오래 유...

Aug 12, 20252 min read

gaeng

22 posts