포스트

JAVA (1) - JVM 구조와 자바 실행 원리

JAVA (1) - JVM 구조와 자바 실행 원리

1. 자바 실행 과정 (Java Code to Execution)

자바는 “Write Once, Run Anywhere (한 번 작성하면 어디서든 실행된다)”라는 슬로건을 가진 대표적인 플랫폼 독립적 언어다. 이를 가능하게 만드는 것이 바로 컴파일 과정JVM이다. 자바 소스 코드가 작성된 후 실행되기까지의 전체 흐름은 다음과 같다.

  1. 소스 코드 작성 (.java): 개발자가 자바 문법에 맞추어 코드 파일을 작성한다.
  2. 자바 컴파일러(javac) 컴파일: javac 명령어를 통해 소스 코드를 읽어 자바 바이트코드(Bytecode, .class) 파일로 변환한다. 바이트코드는 아직 컴퓨터가 직접 이해할 수 없는, JVM이 이해할 수 있는 중간 단계의 기계어다.
  3. 클래스 로더(Class Loader) 로딩: 작성된 .class 파일들을 JVM 내부로 로딩하고 링크 및 초기화를 거쳐 메모리(Runtime Data Area)에 적재한다.
  4. 실행 엔진(Execution Engine) 해석: JVM의 실행 엔진이 메모리에 올려진 바이트코드를 OS가 이해할 수 있는 네이티브 기계어로 해석하고 실행한다.

2. JVM 내부 구조 (JVM Architecture)

JVM은 크게 클래스 로더 System, 런타임 데이터 영역(Runtime Data Area), 실행 엔진(Execution Engine), 네이티브 인터페이스(JNI/Native Method Libraries)로 구성된다.

① 클래스 로더 (Class Loader)

동적 로딩(Dynamic Loading)을 담당하는 주체다. 자바는 컴파일 시점에 모든 클래스를 로딩하는 것이 아니라, 애플리케이션 실행 중 필요한 시점에 .class 파일을 읽어 메모리에 적재한다.

  • Loading: 클래스 파일을 가져와 JVM 메모리에 올린다.
  • Linking: Verify(검증) $\rightarrow$ Prepare(메모리 할당) $\rightarrow$ Resolve(심볼릭 참조를 실제 메모리 주소로 변경) 과정을 진행한다.
  • Initialization: static 변수 및 블록을 초기화한다.

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

JVM이 OS로부터 할당받은 메모리 공간이다. 목적에 따라 5가지 주요 영역으로 나뉘며, 스레드 간 공유 여부가 매우 중요하다.

모든 스레드가 공유하는 영역

  • Method Area (메서드 영역): 클래스 구조 정보(클래스명, 부모 클래스명, 메서드/필드 데이터), static 변수, Constant Pool(상수 풀) 등이 저장된다. JVM 시작 시 생성된다.
  • Heap Area (힙 영역): new 키워드로 생성된 모든 객체(Instance)와 배열이 저장되는 영역이다. 가비지 컬렉터(Garbage Collector)의 주요 관리 대상이다.

스레드마다 독립적으로 생성되는 영역

  • JVM Stack (JVM 스택): 메서드가 호출될 때마다 Frame이라는 단위가 쌓인다. 프레임 내부에는 지역 변수, 매개변수, 연산 스택, 리턴 값 등이 보관된다. 메서드가 종료되면 해당 프레임은 스택에서 pop되어 제거된다.
  • PC Register (Program Counter Register): 현재 스레드가 실행 중인 JVM 명령의 메모리 주소를 가리킨다.
  • Native Method Stack: 자바 외의 언어(C, C++ 등)로 작성된 네이티브 코드를 실행할 때 사용되는 스택이다.

③ 실행 엔진 (Execution Engine)

메모리에 적재된 바이트코드를 실행하는 주체다.

  • Interpreter (인터프리터): 바이트코드를 한 줄씩 읽어서 기계어로 해석하고 실행한다. 초기 실행 속도는 빠르지만, 동일한 코드가 반복 호출되어도 매번 재해석하므로 전반적인 실행 속도가 느리다.
  • JIT Compiler (Just-In-Time Compiler): 인터프리터의 단점을 보완하기 위해 도입되었다. 자주 실행되는 코드(Hot Spot)를 감지하여 바이트코드 전체를 네이티브 기계어로 미리 컴파일해두고, 이후에는 해석 없이 직접 기계어를 실행한다.
  • Garbage Collector (GC): Heap 영역에서 더 이상 참조되지 않는 객체들을 탐색하여 메모리에서 해제한다.

④ JNI & Native Method Libraries

  • JNI (Java Native Interface): C, C++ 등의 네이티브 라이브러리와 상호작용할 수 있는 인터페이스를 제공한다.
  • Native Method Libraries: C/C++로 구현된 라이브러리 모음이다.

3. WORA(Write Once, Run Anywhere)의 핵심 원리

C/C++ 같은 컴파일 언어는 소스 코드를 특정 OS(Windows, Linux, macOS)에 맞춰 직접 기계어로 컴파일한다. 따라서 OS마다 컴파일 결과물이 달라지고 바이너리가 타깃 플랫폼에 귀속된다.

반면 자바는 OS에 독립적인 중간 단계인 바이트코드를 생성한다. 그리고 각 OS 환경에 맞게 제작된 JVM이 이 바이트코드를 받아 해당 OS의 기계어로 번역해 실행한다. 즉, OS 종속적인 작업은 JVM이 전담하므로 개발자는 플랫폼에 신경 쓰지 않고 동일한 바이트코드만 배포하면 어디서든 동일하게 동작시킬 수 있다.


4. 핵심 요약

구성 요소역할 및 핵심 기능
javac.java 소스 코드를 .class 자바 바이트코드로 컴파일한다.
Class Loader바이트코드 파일들을 런타임 시점에 동적으로 메모리에 올린다.
Method Area클래스 메타 데이터, static 변수를 저장하며 모든 스레드가 공유한다.
Heap동적으로 생성된 객체가 저장되는 영역으로, GC의 관리를 받는다.
Stack / PC / Native각 스레드마다 독립적으로 존재하며 메서드 호출 정보 및 실행 상태를 관리한다.
JIT Compiler자주 쓰이는 바이트코드를 네이티브 기계어로 변환하여 실행 성능을 끌어올린다.
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.